package com.xixun.xixunplayer; import android.annotation.SuppressLint; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.StrictMode; import android.view.Choreographer; import android.view.View; import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import com.xixun.joey.aidlset.CardService; import com.xixun.xy.conn.aidl.ConnService; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import gnph.util.Chsets; import gnph.util.JSList; import gnph.util.JSMap; public class MainActivity extends Activity implements Choreographer.FrameCallback { public static MainActivity ins; ArrayList reces = new ArrayList<>(); ArrayList services = new ArrayList<>(); public Intent environIntent = new Intent(); HashSet environs = new HashSet<>(); BackView backView; Prog progView, insView; long launchMilli = System.currentTimeMillis(); long syncMs; int state; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ins = this; Util.println("==>> MainActivity onCreate >>>> this "+hashCode()+" Thread: "+Thread.currentThread().getId()); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build()); setContentView(backView = new BackView(MainActivity.this)); state = 5; hookWebView(); if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init(); else { Util.println("---- No permission, Try again ..."); Util.makeText(this, "No permission, Try again ...").show(); ActivityCompat.requestPermissions(this, new String[]{ android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, android.Manifest.permission.RECEIVE_BOOT_COMPLETED, android.Manifest.permission.INTERNET }, 999); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(Util.programDir!=null) return; if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED) init(); else { Util.println("---- Request Permission Failed"); Util.makeText(this, "Request Permission Failed").show(); } } @SuppressLint("SoonBlockedPrivateApi") public static void hookWebView() { if(android.os.Process.myUid() != android.os.Process.SYSTEM_UID) return; try { var factoryClass = Class.forName("android.webkit.WebViewFactory"); var field = factoryClass.getDeclaredField("sProviderInstance"); field.setAccessible(true); var sProviderInstance = field.get(null); if (sProviderInstance != null) { Util.println("sProviderInstance isn't null"); return; } Method getProviderClassMethod; if (Build.VERSION.SDK_INT > 22) { // above 22 getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass"); } else if (Build.VERSION.SDK_INT == 22) { // method name is a little different getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass"); } else { // no security check below 22 Util.println("Don't need to Hook WebView"); return; } getProviderClassMethod.setAccessible(true); var providerClass = (Class) getProviderClassMethod.invoke(factoryClass); var delegateClass = Class.forName("android.webkit.WebViewDelegate"); var declaredConstructor = delegateClass.getDeclaredConstructor(); declaredConstructor.setAccessible(true); sProviderInstance = providerClass.getDeclaredMethod("create", delegateClass).invoke(providerClass, declaredConstructor.newInstance()); field.set("sProviderInstance", sProviderInstance); Util.println("sProviderInstance"+sProviderInstance.toString()); Util.println("Hook Done!"); } catch (Throwable e) { Util.printStackTrace(e); } } @Override protected void onDestroy() { super.onDestroy(); Util.println("==<< MainActivity onDestroy <<<< this "+hashCode()); state = 8; if(insView!=null) { insView.release(); insView = null; } if(progView!=null) { progView.release(); progView = null; } ins = null; for(var rece : reces) unregisterReceiver(rece); for(var service : services) unbindService(service); try (var fout = new FileOutputStream(Util.programDir+"/cfg")){ Util.cfg.write(fout); fout.flush(); fout.getFD().sync(); } catch (Exception ignored) { } System.gc(); } @Override protected void onStart() { super.onStart(); Util.println(" ==>> MainActivity onStart >> "+hashCode()); } @Override protected void onStop() { super.onStop(); Util.println(" ==<< MainActivity onStop << "+hashCode()); } @Override protected void onRestart() { super.onRestart(); Util.println(" ==>> MainActivity onRestart >> "+hashCode()); } @Override protected void onResume() { super.onResume(); Util.println(" ==>> MainActivity onResume >> "+hashCode()); } @Override protected void onPause() { super.onPause(); Util.println(" ==<< MainActivity onPause << "+hashCode()); } CardService serviCard; ConnService serviXy; Intent intenCard; ServiceConnection connCard; @SuppressLint("UnspecifiedRegisterReceiverFlag") public void init() { Util.initDir(this); if(MainService.ins==null) startService(new Intent(this, MainService.class)); var cfg = new File(Util.programDir+"/cfg"); if(! cfg.isFile()) Util.cfg = new JSMap(); else { try { Util.cfg = JSMap.fromClose(new FileInputStream(cfg)); } catch (Exception e) { Util.cfg = new JSMap(); Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.printStackTrace(e); } } Util.println(Util.cfg.toStr()); Util.logOn = Util.cfg.get("logUploadUrl")!=null; Util.openHelper = new SQLiteOpenHelper(this, "aaa", null, 1) { @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table cfg(k varchar(32) primary key, v varchar(128))"); db.execSQL("create table log(time integer primary key, dur integer, id varchar(64), name varchar(64), type varchar(16), lat double, lng double)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }; new LogThread().start(); if(Util.logOn) { var db = Util.openHelper.getWritableDatabase(); var cursor = db.rawQuery("select count(*) aaa from log", null); while(cursor.moveToNext()) Util.println("Log Cnt "+cursor.getString(0)); db.close(); } connCard = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { serviCard = null; Util.println("<-<- AIDL Service cardsystem Disconnected"); } public void onServiceConnected(ComponentName name, IBinder iBinder) { unbindService(this); services.remove(this); Util.println("->-> AIDL Service cardsystem Connected"); serviCard = CardService.Stub.asInterface(iBinder); try { Util.isScreenOn = serviCard.isScreenOpen(); Util.screenWidth = serviCard.getScreenWidth(); Util.screenHeight = serviCard.getScreenHeight(); Util.println(" IsScreenOn: "+Util.isScreenOn+" screen: "+Util.screenWidth+" x "+Util.screenHeight); backView.invalidate(); if(Util.isScreenOn) initProg(); else state = 8; } catch (Exception e) { Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.printStackTrace(e); } } }; intenCard = new Intent("com.xixun.joey.aidlset.SettingsService"); intenCard.setPackage("com.xixun.joey.cardsystem"); bindService(intenCard, connCard, Context.BIND_AUTO_CREATE); services.add(connCard); var connXy = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { serviXy = null; Util.println("<-<- AIDL Service xy.conn Disconnected"); } public void onServiceConnected(ComponentName name, IBinder iBinder) { unbindService(this); services.remove(this); Util.println("->-> AIDL Service xy.conn Connected"); serviXy = ConnService.Stub.asInterface(iBinder); try { Util.serverURL = serviXy.getServerURL(); Util.println(" ServerURL: "+Util.serverURL); if(Util.serverURL==null || Util.serverURL.isEmpty()) Util.serverURL = "https://m2mled.net/"; else { if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL; if(! Util.serverURL.endsWith("/")) Util.serverURL += "/"; } } catch (Exception e) { Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.printStackTrace(e); } } }; var intenXy = new Intent("xixun.intent.action.CONNECTION_INFO"); intenXy.setPackage("com.xixun.xy.conn"); bindService(intenXy, connXy, Context.BIND_AUTO_CREATE); services.add(connXy); reces.clear(); BroadcastReceiver rece; registerReceiver(rece = new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { Util.println("Receive PAUSE_PLAYER"); Util.isScreenOn = ! intent.getBooleanExtra("pause", false); Util.println(" IsScreenOn: "+Util.isScreenOn); if(! Util.isScreenOn) { state = 8; if(insView!=null) { insView.release(); insView = null; } if(progView!=null) { progView.release(); progView = null; } setContentView(backView); } else if(progView==null && insView==null) MainActivity.ins.runOnUiThread(() -> initProg()); System.gc(); } }, new IntentFilter("com.xixun.action.PAUSE_PLAYER")); reces.add(rece); registerReceiver(rece = new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { Util.println("Receive CHANGE_COMPANYID"); try { if(serviXy!=null && serviXy.asBinder().isBinderAlive()) { Util.serverURL = serviXy.getServerURL(); Util.println(" ServerURL: "+Util.serverURL); if(Util.serverURL==null || Util.serverURL.isEmpty()) Util.serverURL = "https://m2mled.net/"; else { if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL; if(! Util.serverURL.endsWith("/")) Util.serverURL += "/"; } } else { Util.println(" bindService"); bindService(intenXy, connXy, Context.BIND_AUTO_CREATE); } } catch (Exception e) { Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.printStackTrace(e); } } }, new IntentFilter("com.xixun.joey.CHANGE_COMPANYID")); reces.add(rece); registerReceiver(rece = new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { MainActivity.this.environIntent = intent; for(var environ : environs) { environ.onReceive(intent); ((View)environ).invalidate(); } } }, new IntentFilter("xixun.intent.action.TEMPERATURE_HUMIDITY")); reces.add(rece); registerReceiver(rece = new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { Util.lat = intent.getDoubleExtra("latitude", 0); Util.lng = intent.getDoubleExtra("longitude", 0); Util.println(" lat "+Util.lat+" lng "+Util.lng); } }, new IntentFilter("com.xixun.joey.gpsinfo")); reces.add(rece); registerReceiver(rece = new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { try { var code = intent.getIntExtra("code", 0); Util.println(" remote_control "+code); if(progView == null || progView.pages.isEmpty() || ! progView.isShown() || code==-1 || code < -3 || code > progView.pages.size()) return; if(code<=-2) { var idx = avas.isEmpty() ? -1 : progView.pages.indexOf(avas.get(curAva)); if(code==-2) code = idx<=0 ? progView.pages.size() : idx; else code = idx>=progView.pages.size()-1 ? 1 : idx+2; } var ms = System.currentTimeMillis(); if(! avas.isEmpty()) { avas.get(curAva).hide(); if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) for(var call : progView.calls) call.doFrame(ms); } if(code==0) syncProg(ms, 0); else { avas.clear(); var page = progView.pages.get(code-1); avas.add(page); curAva = 0; curTimes = 1; waitTo = 0; //点播 page.setMillis(ms, ms); if(state != 6) { setContentView(progView); state = 6; } } var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program"))); root.put("Demand", code); try (var fOut = new FileOutputStream(Util.programDir + "/program")) { root.write(fOut); fOut.flush(); fOut.getFD().sync(); } } catch (Throwable e) { Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.printStackTrace(e); } } }, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL")); reces.add(rece); if(Util.custom==Util.Custom.Yishi) { new Thread(()->{ while(true) { try { Thread.sleep(10000); if(serviCard!=null && serviCard.asBinder().isBinderAlive()) { var www = serviCard.getScreenWidth(); var hhh = serviCard.getScreenHeight(); if(www!=Util.screenWidth || hhh!=Util.screenHeight) { var ins = MainActivity.ins; if(ins!=null) ins.runOnUiThread(() -> { Util.screenWidth = www; Util.screenHeight = hhh; ins.backView.invalidate(); }); } } else { Util.println(" bindService"); var ins = MainActivity.ins; if(ins!=null) ins.runOnUiThread(() -> ins.bindService(ins.intenCard, ins.connCard, Context.BIND_AUTO_CREATE)); } } catch (Throwable e) { var ins = MainActivity.ins; if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show()); Util.printStackTrace(e); } } }).start(); } } public void stopProg() { avas.clear(); curAva = 0; curTimes = 1; if(insView!=null) { insView.release(); insView = null; } if(progView!=null) { progView.release(); progView = null; } setContentView(backView); System.gc(); } public boolean delProgFile() { stopProg(); var files = new File(Util.programDir).listFiles(); var ok = true; if(files != null) for(var file : files) if(! file.delete()) ok = false; state = 4; try { var out = new FileOutputStream(Util.programDir+"/program"); out.write("{}".getBytes()); out.flush(); out.getFD().sync(); out.close(); } catch (Throwable ignored) { } return ok; } public void initProg() { state = 1; try { Util.println("\nParse Insert Prog Json"); var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/insert"))); var task = root.jsmap("task"); if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root))); if(task!=null) { var view = new Prog(task, this); if(view.getChildCount()!=0) setContentView(insView = view); } } catch(FileNotFoundException ignored) { } catch(Throwable e) { state = 7; Util.printStackTrace(e); } var demand = 0; try { Util.println("\nParse Prog Json"); var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program"))); var task = root.jsmap("task"); demand = root.intg("Demand"); if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root))); if(task==null) Util.println(root.isEmpty() ? " Empty program JSON\n" : " Error: task==null\n"); else { var view = new Prog(task, this); if(view.getChildCount()==0) Util.println(" Error: ChildCount==0\n"); else setContentView(progView = view); } } catch(FileNotFoundException e) { Util.println(""+e); } catch(Throwable e) { state = 7; Util.makeText(this, Util.toStr(e)).show(); Util.printStackTrace(e); } if(insView!=null || progView!=null) { state = 5; Util.println("Init Sync"); var ms = System.currentTimeMillis(); if(demand==0 || progView==null) syncProg((ms+999)/1000*1000, 0); else { avas.clear(); var page = progView.pages.get(demand-1); avas.add(page); curAva = 0; curTimes = 1; waitTo = 0; //点播 page.setMillis(ms, ms); if(state != 6) { setContentView(progView); state = 6; } } if(canAdd) { choreographer.postFrameCallback(this); canAdd = false; } } else if(state != 7) state = 3; } public void initProg(byte[] json) { try { Util.println("\nParse Prog Json"); var root = JSMap.from(json); var task = root.jsmap("task"); if(task==null) { if(! root.containsKey("layers")) { state = 7; Util.println(" Error: task==null\n"); Util.println(new String(json, Chsets.UTF8)); return; } task = new JSMap("items", new JSList<>(new JSMap("_program", root))); } var view = new Prog(task, this); if(view.getChildCount()==0) { if(! view.isInsert) state = 7; Util.println(" Error: ChildCount==0\n"); Util.println(new String(json, Chsets.UTF8)); return; } if(Util.cfg.bool("needCheck")) { new Thread(()->{ if(Util.check(view)) runOnUiThread(() -> { afterCheck(view, json); }); else runOnUiThread(() -> { view.release(); Util.makeText(this, "Contents are not passed").show(); Util.println("Contents are not passed"); }); }).start(); } else afterCheck(view, json); } catch (Throwable e) { state = 7; Util.makeText(this, Util.toStr(e)).show(); Util.printStackTrace(e); Util.println(new String(json, Chsets.UTF8)); } } public void afterCheck(Prog view, byte[] json) { try { if(view.isInsert) { if(insView!=null) insView.release(); insView = view; try { avas.get(curAva).hide();} catch (Throwable ignored) {} setContentView(insView); } else { if(progView!=null) progView.release(); progView = view; setContentView(progView); } var fOut = new FileOutputStream(Util.programDir + (view.isInsert?"/insert":"/program")); fOut.write(json); fOut.flush(); fOut.getFD().sync(); fOut.close(); state = 5; System.gc(); Util.println("Init Sync"); syncProg((System.currentTimeMillis()+999)/1000*1000, 0); if(canAdd) { choreographer.postFrameCallback(this); canAdd = false; } } catch (Throwable e) { state = 7; Util.makeText(this, Util.toStr(e)).show(); Util.printStackTrace(e); Util.println(new String(json, Chsets.UTF8)); } } ArrayList avas = new ArrayList<>(); int curAva, curTimes = 1; long waitTo = Long.MAX_VALUE; boolean isInsert; Choreographer choreographer = Choreographer.getInstance(); boolean canAdd = true; @Override public void doFrame(long frameTimeNanos) { if(progView == null && insView==null) { canAdd = true; return; } choreographer.postFrameCallback(this); canAdd = false; var milli = System.currentTimeMillis(); if(avas.isEmpty()) { if(milli >= waitTo) { waitTo = Long.MAX_VALUE; Util.println("wait sync"); syncProg(milli, 0); if(insView!=null) for(var call : insView.calls) call.doFrame(milli); if(progView!=null) for(var call : progView.calls) call.doFrame(milli); } return; } var lastPage = avas.get(curAva); if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli); else { lastPage.hide(); if(isInsert) { if(--lastPage.repeatTimes<=0 && ++curAva >= avas.size()) { var isDiff = milli-lastPage.endMilli>=1000; syncProg(isDiff ? milli : lastPage.endMilli, milli); if(insView!=null) for(var call : insView.calls) call.doFrame(milli); if(progView!=null) for(var call : progView.calls) call.doFrame(milli); return; } } else { if(waitTo > 0) { //waitTo==0 为点播,不换页 if(curTimes < lastPage.repeatTimes) curTimes++; else { curTimes = 1; if(++curAva >= avas.size()) { var isDiff = milli-lastPage.endMilli>=1000; syncProg(isDiff ? milli : lastPage.endMilli, milli); if(insView!=null) for(var call : insView.calls) call.doFrame(milli); if(progView!=null) for(var call : progView.calls) call.doFrame(milli); return; } } } else if(milli-lastPage.endMilli >= 5000) { Util.println("System Time Changed: "+Util.dateFmt.format(lastPage.endMilli)+" -> "+Util.dateFmt.format(milli)); lastPage.endMilli = milli; } } avas.get(curAva).setMillis(lastPage.endMilli, milli); Util.println("curAva: "+curAva+" endMs: "+avas.get(curAva).endMilli); } if(isInsert) for(var call : insView.calls) call.doFrame(milli); else for(var call : progView.calls) call.doFrame(milli); } void syncProg(long milli, long cur) { if(cur==0) cur = milli; Util.println("\nSyncProg"); waitTo = Long.MAX_VALUE; avas.clear(); curAva = 0; curTimes = 1; isInsert = false; if(insView!=null) { for(int i=0; i