You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1039 lines
38 KiB
Java
1039 lines
38 KiB
Java
package com.xypower.mpmaster;
|
|
|
|
import android.app.AlarmManager;
|
|
import android.app.Notification;
|
|
import android.app.NotificationChannel;
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.app.Service;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.pm.PackageInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.net.ConnectivityManager;
|
|
import android.os.BatteryManager;
|
|
import android.os.Build;
|
|
import android.os.Environment;
|
|
import android.os.Handler;
|
|
import android.os.IBinder;
|
|
import android.os.Message;
|
|
import android.os.PowerManager;
|
|
import android.telephony.SignalStrength;
|
|
import android.telephony.SubscriptionInfo;
|
|
import android.telephony.SubscriptionManager;
|
|
import android.telephony.TelephonyManager;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
import android.widget.RemoteViews;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.core.app.NotificationCompat;
|
|
|
|
import com.dev.devapi.api.SysApi;
|
|
import com.xypower.common.FileDownloader;
|
|
import com.xypower.common.JSONUtils;
|
|
import com.xypower.common.MicroPhotoContext;
|
|
import com.xypower.mpmaster.sms.SimUtil;
|
|
import com.xypower.mpmaster.sms.SmsSendReceiver;
|
|
|
|
import org.json.JSONObject;
|
|
|
|
import java.io.File;
|
|
import java.lang.reflect.Method;
|
|
import java.text.SimpleDateFormat;
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDateTime;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.Date;
|
|
import java.util.List;
|
|
import java.util.logging.FileHandler;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
public class MpMasterService extends Service {
|
|
static {
|
|
System.loadLibrary("mpmaster");
|
|
}
|
|
|
|
public static final String TAG = "MPMST";
|
|
|
|
public Logger logger;
|
|
|
|
public static final int NOTIFICATION_ID_FOREGROUND_SERVICE = 8466503;
|
|
public static final String ACTION_MSG_BROADCAST = "ACT_MSG_BROADCAST";
|
|
|
|
public static final String ACTION_START = "com.xypower.mpmaster.ACT_START";
|
|
public static final String ACTION_STOP = "com.xypower.mpmaster.ACT_STOP";
|
|
public static final String ACTION_MAIN = "com.xypower.mpmaster.ACT_MAIN";
|
|
|
|
public static final String ACTION_UPD_OTA = SysApi.OTA_RESULT_ACTION;
|
|
public static final String ACTION_INSTALL_RESULT = SysApi.INSTALL_RESULT_ACTION;
|
|
public static final String ACTION_UNINSTALL_RESULT = SysApi.UNINSTALL_RESULT_ACTION;
|
|
|
|
private static final String ACTION_UPDATE_CONFIGS = "com.xypower.mpmaster.ACT_UPD_CFG";
|
|
|
|
private static final String ACTION_HEARTBEAT = "com.xypower.mpmaster.ACT_HB";
|
|
private static final String ACTION_TAKE_PHOTO = "com.xypower.mpapp.ACT_TP";
|
|
|
|
public static final String ACTION_MP_RESTART = "com.xypower.mpapp.ACT_START";
|
|
public static final String ACTION_IMP_PUBKRY = "com.xypower.mpapp.ACT_IMP_PUBKEY";
|
|
|
|
private static final String FOREGROUND_CHANNEL_ID = "foreground_channel_id";
|
|
private SmsSendReceiver mSmsSnedReceiver;
|
|
|
|
private int mPrevDateForLogs = 0;
|
|
|
|
public static class STATE_SERVICE {
|
|
public static final int CONNECTED = 10;
|
|
public static final int NOT_CONNECTED = 0;
|
|
}
|
|
|
|
private static int mStateService = STATE_SERVICE.NOT_CONNECTED;
|
|
|
|
private int mMpHeartbeatDuration = 10; // Unit: minute
|
|
|
|
private boolean mMntnMode = false;
|
|
private boolean mQuickHbMode = false;
|
|
private boolean mUsingAbsHbTime = false;
|
|
private String mCmdid = "";
|
|
private NotificationManager mNotificationManager;
|
|
private int mQuickHeartbeatDuration = 60; // Unit: second
|
|
private int mHeartbeatDuration = 600; // Unit: second 10m = 10 * 60s
|
|
|
|
private long mTimeForKeepingLogs = 86400000 * 15; // 15 days
|
|
|
|
private AlarmReceiver mAlarmReceiver = null;
|
|
private ScreenActionReceiver mScreenaAtionReceiver = null;
|
|
private UpdateReceiver mUpdateReceiver = null;
|
|
|
|
private Handler mHander = null;
|
|
|
|
private String mModelName = null;
|
|
|
|
private static String mMpAppVersion = null;
|
|
private static String mMpMasterVersion = null;
|
|
|
|
private String mSerialNo = null;
|
|
|
|
private long mTimeToStartMpApp = 0;
|
|
private long mTimeOfMpAppAlive = 1800000; // 30minutes
|
|
private int mAbsHeartbeatTimes[] = null;
|
|
|
|
private boolean mSeparateNetwork = false;
|
|
|
|
private PendingIntent mPreviousHB = null;
|
|
private long mPreviousHeartbeatTime = 0;
|
|
|
|
private long mPreviousMpHbTime = 0;
|
|
|
|
private String mIccid1 = null;
|
|
private String mIccid2 = null;
|
|
|
|
public MpMasterService() {
|
|
}
|
|
|
|
@Override
|
|
public IBinder onBind(Intent intent) {
|
|
// TODO: Return the communication channel to the service.
|
|
throw new UnsupportedOperationException("Not yet implemented");
|
|
}
|
|
|
|
@Override
|
|
public void onCreate() {
|
|
super.onCreate();
|
|
|
|
loadConfig();
|
|
loadIccid();
|
|
|
|
logger = Logger.getLogger("com.xypower.mpmaster.logger");
|
|
logger.setLevel(Level.ALL);
|
|
// LogFormatter.installFormatter(logger);
|
|
LogFormatter logFormatter = new LogFormatter();
|
|
|
|
if (BuildConfig.DEBUG) {
|
|
LogcatHandler logcatHandler = new LogcatHandler(TAG);
|
|
logcatHandler.setFormatter(logFormatter);
|
|
logger.addHandler(logcatHandler);
|
|
}
|
|
|
|
RotatingHandler rotatingHandler = null;
|
|
try {
|
|
String appPath = MicroPhotoContext.buildMasterAppDir(getApplicationContext());
|
|
String logPath = appPath + "logs";
|
|
File fi = new File(logPath);
|
|
if (!fi.exists()) {
|
|
fi.mkdirs();
|
|
}
|
|
|
|
File logFile = new File(fi, "mlog.txt");
|
|
|
|
rotatingHandler = new RotatingHandler(logFile.getAbsolutePath(), logFormatter);
|
|
|
|
logger.addHandler(rotatingHandler);
|
|
|
|
} catch (Throwable e) {
|
|
System.out.println("Failed to create directory:" + e.getMessage());
|
|
}
|
|
|
|
try {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
mSerialNo = Build.getSerial();
|
|
} else {
|
|
mSerialNo = Build.SERIAL;
|
|
}
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
|
|
mMpMasterVersion = MicroPhotoContext.getVersionName(getApplicationContext());
|
|
PackageManager packageManager = getPackageManager();
|
|
PackageInfo packageInfo = null;
|
|
try {
|
|
packageInfo = packageManager.getPackageInfo(MicroPhotoContext.PACKAGE_NAME_MPAPP, 0);
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
mMpAppVersion = packageInfo == null ? "" : packageInfo.versionName;
|
|
|
|
logger.info("MpMaster started version=" + mMpMasterVersion);
|
|
|
|
mHander = new Handler();
|
|
|
|
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
|
mStateService = STATE_SERVICE.NOT_CONNECTED;
|
|
|
|
mScreenaAtionReceiver = new ScreenActionReceiver();
|
|
|
|
// 注册广播接受者
|
|
{
|
|
mAlarmReceiver = new AlarmReceiver(this);
|
|
IntentFilter intentFilter = new IntentFilter(ACTION_HEARTBEAT);
|
|
intentFilter.addAction(ACTION_MSG_BROADCAST);
|
|
intentFilter.addAction(ACTION_UPDATE_CONFIGS);
|
|
intentFilter.addAction(ACTION_UPD_OTA);
|
|
intentFilter.addAction(ACTION_INSTALL_RESULT);
|
|
intentFilter.addAction(ACTION_UNINSTALL_RESULT);
|
|
intentFilter.addAction(MicroPhotoContext.ACTION_HEARTBEAT_MP);
|
|
|
|
registerReceiver(mAlarmReceiver, intentFilter);
|
|
}
|
|
|
|
{
|
|
mUpdateReceiver = new UpdateReceiver();
|
|
IntentFilter intentFilter = new IntentFilter();
|
|
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
|
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
|
intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
|
|
intentFilter.addDataScheme("package");
|
|
registerReceiver(mUpdateReceiver, intentFilter);
|
|
}
|
|
|
|
// SMS broadcast receiver
|
|
mSmsSnedReceiver = new SmsSendReceiver();
|
|
IntentFilter intentFilter;
|
|
intentFilter = new IntentFilter(SimUtil.SMS_SEND_ACTION);
|
|
registerReceiver(mSmsSnedReceiver, intentFilter);
|
|
}
|
|
|
|
public String getIccid(int number) {
|
|
if (number == 1) {
|
|
return mIccid1 == null ? "" : mIccid1;
|
|
} else {
|
|
return mIccid2 == null ? "" : mIccid2;
|
|
}
|
|
}
|
|
|
|
public void cleanLogFiles() {
|
|
|
|
try {
|
|
Date dt = new Date();
|
|
|
|
if (dt.getDate() == mPrevDateForLogs) {
|
|
return;
|
|
}
|
|
|
|
String appPath = MicroPhotoContext.buildMasterAppDir(getApplicationContext());
|
|
String logPath = appPath + "logs";
|
|
File fi = new File(logPath);
|
|
if (!fi.exists()) {
|
|
return;
|
|
}
|
|
|
|
mPrevDateForLogs = dt.getDate();
|
|
dt.setHours(0);
|
|
dt.setMinutes(0);
|
|
long millis = dt.getTime() - mTimeForKeepingLogs;
|
|
File[] subFiles = fi.listFiles();
|
|
for (File f : subFiles) {
|
|
if (f.lastModified() < millis) {
|
|
f.delete();
|
|
}
|
|
}
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
|
|
mStateService = STATE_SERVICE.NOT_CONNECTED;
|
|
|
|
logger.warning("MicroPhotoService::onDestroy called");
|
|
|
|
unregisterReceiver(mAlarmReceiver);
|
|
unregisterReceiver(mScreenaAtionReceiver);
|
|
unregisterReceiver(mUpdateReceiver);
|
|
unregisterReceiver(mSmsSnedReceiver);
|
|
|
|
for (java.util.logging.Handler h : logger.getHandlers()) {
|
|
try {
|
|
h.close();
|
|
} catch (Exception ex) {
|
|
}
|
|
}
|
|
|
|
super.onDestroy();
|
|
}
|
|
|
|
protected void loadConfig() {
|
|
MicroPhotoContext.MasterConfig masterConfig = MicroPhotoContext.getMasterConfig(getApplicationContext());
|
|
mMntnMode = masterConfig.mntnMode != 0;
|
|
mQuickHbMode = masterConfig.quickHbMode != 0;
|
|
mUsingAbsHbTime = masterConfig.usingAbsHbTime != 0;
|
|
mHeartbeatDuration = masterConfig.heartbeat * 60; // minute to second
|
|
mQuickHeartbeatDuration = masterConfig.quickHeartbeat;
|
|
mAbsHeartbeatTimes = masterConfig.absHeartbeats;
|
|
if (mAbsHeartbeatTimes != null && mAbsHeartbeatTimes.length > 0) {
|
|
Arrays.sort(mAbsHeartbeatTimes);
|
|
}
|
|
mSeparateNetwork = masterConfig.separateNetwork != 0;
|
|
mTimeOfMpAppAlive = masterConfig.mpappMonitorTimeout;
|
|
if (masterConfig.timeForKeepingLogs > 0) {
|
|
mTimeForKeepingLogs = masterConfig.timeForKeepingLogs * 86400000;
|
|
}
|
|
}
|
|
|
|
private void loadIccid() {
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
|
|
try {
|
|
SubscriptionManager sm = SubscriptionManager.from(this);
|
|
List<SubscriptionInfo> sis = sm.getActiveSubscriptionInfoList();
|
|
if (sis.size() >= 1) {
|
|
SubscriptionInfo si1 = sis.get(0);
|
|
mIccid1 = si1.getIccId();
|
|
// String phoneNum1 = si1.getNumber();
|
|
}
|
|
if (sis.size() >= 2) {
|
|
SubscriptionInfo si2 = sis.get(1);
|
|
mIccid2 = si2.getIccId();
|
|
// String phoneNum2 = si2.getNumber();
|
|
}
|
|
// int count = sm.getActiveSubscriptionInfoCount();// real cards
|
|
// int max = sm.getActiveSubscriptionInfoCountMax();// Slot number
|
|
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
// public boolean useSeparater
|
|
public String getCmdid() {
|
|
return mCmdid;
|
|
}
|
|
|
|
public boolean isSeparateNetwork() {
|
|
return mSeparateNetwork;
|
|
}
|
|
|
|
public boolean isMntnMode() {
|
|
return mMntnMode;
|
|
}
|
|
|
|
public void startMpApp() {
|
|
try {
|
|
final Context context = getApplicationContext();
|
|
|
|
if (MicroPhotoContext.isAppAlive(context, MicroPhotoContext.PACKAGE_NAME_MPAPP)) {
|
|
logger.warning("MpAPP is STILL Running");
|
|
return;
|
|
}
|
|
|
|
String appPath = MicroPhotoContext.buildMpAppDir(context);
|
|
long ts = System.currentTimeMillis();
|
|
if (ts - mTimeToStartMpApp < 30000) {
|
|
logger.warning("MpAPP is STILL Running");
|
|
return;
|
|
}
|
|
|
|
File mpappHb = new File(appPath + "data/alive/hb");
|
|
long modifiedTimeOfHb = getFileModificationTime(appPath + "data/alive/hb");
|
|
long modifiedTimeOfPhoto = getFileModificationTime(appPath + "data/alive/taking");
|
|
long modifiedTimeOfUpload = getFileModificationTime(appPath + "data/alive/upload");
|
|
|
|
if (((ts - modifiedTimeOfHb) > mTimeOfMpAppAlive) ||
|
|
((ts - modifiedTimeOfPhoto) > mTimeOfMpAppAlive * 4) ||
|
|
((ts - modifiedTimeOfUpload) > mTimeOfMpAppAlive * 4)) {
|
|
// greater than 30m
|
|
logger.warning("Restart MpAPP as it is NOT Running hb=" + Long.toString(ts - modifiedTimeOfHb) +
|
|
" taking=" + Long.toString(ts - modifiedTimeOfPhoto) + " sending=" + Long.toString(ts - modifiedTimeOfUpload));
|
|
MicroPhotoContext.restartMpApp(context);
|
|
mTimeToStartMpApp = ts;
|
|
}
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
|
|
long getFileModificationTime(String path) {
|
|
File file = new File(path);
|
|
long mt = 0;
|
|
if (file.exists()) {
|
|
mt = file.lastModified();
|
|
}
|
|
|
|
return mt;
|
|
}
|
|
|
|
public String getMpAppVersion() {
|
|
if (TextUtils.isEmpty(mMpAppVersion)) {
|
|
PackageManager packageManager = getPackageManager();
|
|
PackageInfo packageInfo = null;
|
|
try {
|
|
packageInfo = packageManager.getPackageInfo(MicroPhotoContext.PACKAGE_NAME_MPAPP, 0);
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
mMpAppVersion = packageInfo == null ? "" : packageInfo.versionName;
|
|
}
|
|
return mMpAppVersion;
|
|
}
|
|
|
|
public static void resetVersions() {
|
|
mMpAppVersion = null;
|
|
mMpMasterVersion = null;
|
|
}
|
|
|
|
public String getMasterAppVersion() {
|
|
if (TextUtils.isEmpty(mMpMasterVersion)) {
|
|
mMpMasterVersion = MicroPhotoContext.getVersionName(getApplicationContext());
|
|
}
|
|
return mMpMasterVersion;
|
|
}
|
|
|
|
public String getSerialNo() {
|
|
return mSerialNo;
|
|
}
|
|
|
|
static String convertSwitch(boolean swtch) {
|
|
return swtch ? "ON" : "OFF";
|
|
}
|
|
|
|
public void setMntnMode(boolean mntnMode, boolean quickHbMode) {
|
|
boolean oldMntnMode = mMntnMode;
|
|
boolean oldQuickHbMode = mQuickHbMode;
|
|
|
|
mMntnMode = mntnMode;
|
|
mQuickHbMode = quickHbMode;
|
|
|
|
if (oldMntnMode != mntnMode || oldQuickHbMode != quickHbMode) {
|
|
MicroPhotoContext.MasterConfig masterConfig = MicroPhotoContext.getMasterConfig(getApplicationContext());
|
|
|
|
masterConfig.mntnMode = mntnMode ? 1 : 0;
|
|
masterConfig.quickHbMode = quickHbMode ? 1 : 0;
|
|
|
|
MicroPhotoContext.saveMasterConfig(getApplicationContext(), masterConfig);
|
|
|
|
logger.warning("MNTN Mode Changed from " + convertSwitch(oldMntnMode) + " to " + convertSwitch(mntnMode)
|
|
+ " Quick Heartbeat from " + convertSwitch(oldQuickHbMode) + " to " + convertSwitch(quickHbMode));
|
|
}
|
|
|
|
if (oldQuickHbMode != quickHbMode) {
|
|
// Cancel cuurent job first
|
|
|
|
if (quickHbMode) {
|
|
registerHeartbeatTimer();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void startMaster(boolean bundleWithMpApp) {
|
|
String masterUrl = MicroPhotoContext.DEFAULT_MASTER_URL;
|
|
|
|
MicroPhotoContext.MasterConfig masterConfig = MicroPhotoContext.getMasterConfig(getApplicationContext());
|
|
String url = masterConfig.getUrl();
|
|
if (!TextUtils.isEmpty(url)) {
|
|
masterUrl = url;
|
|
}
|
|
|
|
MicroPhotoContext.AppConfig appConfig = MicroPhotoContext.getMpAppConfig(getApplicationContext());
|
|
if (appConfig.heartbeat > 0) {
|
|
mMpHeartbeatDuration = appConfig.heartbeat;
|
|
}
|
|
|
|
logger.warning("Start Mntn report: " + masterUrl + " MntnMode=" + (mMntnMode ? "1" : "0") + " QuickHB=" + (mQuickHbMode ? "1" : "0"));
|
|
|
|
AppMaster appMaster = new AppMaster(this, masterUrl, appConfig.cmdid, bundleWithMpApp);
|
|
appMaster.start();
|
|
}
|
|
|
|
public static class AlarmReceiver extends BroadcastReceiver {
|
|
private MpMasterService mService;
|
|
public AlarmReceiver() {
|
|
mService = null;
|
|
}
|
|
public AlarmReceiver(MpMasterService service) {
|
|
mService = service;
|
|
}
|
|
public void onReceive(Context context, Intent intent) {
|
|
String action = intent.getAction();
|
|
if (TextUtils.equals(ACTION_HEARTBEAT, action)) {
|
|
boolean keepAlive = intent.getBooleanExtra("keepAlive", false);
|
|
if (keepAlive) {
|
|
mService.logger.info("KeepAlive Heartbeat Timer Fired ACTION=" + action);
|
|
} else {
|
|
mService.logger.info("Heartbeat Timer Fired ACTION=" + action);
|
|
}
|
|
|
|
mService.mPreviousHB = null;
|
|
mService.mPreviousHeartbeatTime = 0;
|
|
mService.registerHeartbeatTimer();
|
|
|
|
if (!keepAlive) {
|
|
mService.startMaster(false);
|
|
}
|
|
mService.startMpApp();
|
|
|
|
} else if (TextUtils.equals(MicroPhotoContext.ACTION_HEARTBEAT_MP, action)) {
|
|
|
|
mService.mPreviousMpHbTime = System.currentTimeMillis();
|
|
if (intent.hasExtra("HeartbeatDuration")) {
|
|
mService.mMpHeartbeatDuration = intent.getIntExtra("HeartbeatDuration", 10);
|
|
}
|
|
|
|
mService.logger.info("Heartbeat Timer Fired By MpAPP ACTION=" + action);
|
|
|
|
mService.registerHeartbeatTimer();
|
|
|
|
if (!mService.mSeparateNetwork && (!mService.mMntnMode)) {
|
|
mService.startMaster(true);
|
|
}
|
|
mService.startMpApp();
|
|
|
|
|
|
} else if (TextUtils.equals(ACTION_UPDATE_CONFIGS, action)) {
|
|
int restart = intent.getIntExtra("restart", 0);
|
|
mService.logger.info("Update Config Fired ACTION=" + action + " restart=" + restart);
|
|
if (restart != 0) {
|
|
MicroPhotoContext.restartApp(context, context.getPackageName());
|
|
} else {
|
|
mService.loadConfig();
|
|
mService.registerHeartbeatTimer();
|
|
}
|
|
} else if (TextUtils.equals(ACTION_UPD_OTA, action)) {
|
|
|
|
String cmd = intent.getStringExtra("cmd");
|
|
String msg = intent.getStringExtra("msg");
|
|
// mService.logger.info("cmd=" + cmd + " msg=" + msg);
|
|
if("write".equals(cmd)) {
|
|
// int progress = Integer.parseInt(msg);
|
|
}
|
|
else if("update".equals(cmd)) {
|
|
// int progress = Integer.parseInt(msg);
|
|
}
|
|
else if("info".equals(cmd)) {
|
|
}
|
|
else if("error".equals(cmd)) {
|
|
mService.logger.warning("UPD OTA Failed");
|
|
}
|
|
else if("success".equals(cmd)) {
|
|
//confirm to reboot device ??
|
|
mService.logger.warning("UPD OTA Succeeded, will REBOOT device");
|
|
Handler handler = new Handler();
|
|
handler.postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
SysApi.reboot(context);
|
|
}
|
|
}, 1000);
|
|
}
|
|
} else if (TextUtils.equals(ACTION_INSTALL_RESULT, action)) {
|
|
boolean bSucc = intent.getBooleanExtra("succ", false);
|
|
String pkname = intent.getStringExtra("pkname");
|
|
String msg = intent.getStringExtra("msg");
|
|
// Log.e("_otg_","install result bsuc="+bSucc+",pkname="+pkname+",msg="+msg);
|
|
mService.logger.warning("INSTALL APP result =" + bSucc + ",pkname=" + pkname + ",msg=" + msg);
|
|
} else if (TextUtils.equals(ACTION_UNINSTALL_RESULT, action)) {
|
|
boolean bSucc = intent.getBooleanExtra("succ", false);
|
|
String pkname = intent.getStringExtra("pkname");
|
|
String msg = intent.getStringExtra("msg");
|
|
mService.logger.warning("UNINSTALL APP result =" + bSucc + ",pkname=" + pkname + ",msg=" + msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void installOTA(final String path) {
|
|
final Context context = getApplicationContext();
|
|
mHander.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
SysApi.installOTA(context, context.getPackageName(), path);
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
private void registerHeartbeatTimer() {
|
|
|
|
long timeout = mHeartbeatDuration;
|
|
boolean keepAlive = false;
|
|
long currentTimeMs = System.currentTimeMillis();
|
|
if (mMntnMode) {
|
|
if (mQuickHbMode) {
|
|
timeout = mQuickHeartbeatDuration;
|
|
}
|
|
registerHeartbeatTimer(currentTimeMs + timeout * 1000, keepAlive);
|
|
} else {
|
|
long closestTime = -1;
|
|
if (mUsingAbsHbTime) {
|
|
Date dt = new Date();
|
|
long ts = dt.getTime();
|
|
ts -= ts % 1000;
|
|
|
|
dt.setHours(0);
|
|
dt.setMinutes(0);
|
|
dt.setSeconds(0);
|
|
|
|
long zeroPoint = dt.getTime();
|
|
zeroPoint -= zeroPoint % 1000;
|
|
long offsetTs = (ts - zeroPoint) / 1000;
|
|
|
|
if (mAbsHeartbeatTimes != null && mAbsHeartbeatTimes.length > 0) {
|
|
|
|
for (int i = 0; i < mAbsHeartbeatTimes.length; i++) {
|
|
if (mAbsHeartbeatTimes[i] > offsetTs) {
|
|
closestTime = mAbsHeartbeatTimes[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (closestTime == -1) {
|
|
// next day
|
|
closestTime = mAbsHeartbeatTimes[0] + 86400;
|
|
}
|
|
} else {
|
|
closestTime = 9 * 3600 + 13 * 60;
|
|
if (offsetTs > closestTime) {
|
|
closestTime += 86400;
|
|
}
|
|
}
|
|
|
|
if (zeroPoint + closestTime * 1000 > currentTimeMs + mMpHeartbeatDuration * 60000) {
|
|
keepAlive = true;
|
|
registerHeartbeatTimer(currentTimeMs + mMpHeartbeatDuration * 60000, keepAlive);
|
|
} else {
|
|
registerHeartbeatTimer(zeroPoint + closestTime * 1000, keepAlive);
|
|
}
|
|
} else {
|
|
if ((mPreviousHeartbeatTime != 0) && (mPreviousHeartbeatTime - currentTimeMs < mHeartbeatDuration * 1000)) {
|
|
registerHeartbeatTimer(mPreviousHeartbeatTime + mHeartbeatDuration * 1000, keepAlive);
|
|
} else {
|
|
registerHeartbeatTimer(currentTimeMs + timeout * 1000, keepAlive);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void registerHeartbeatTimer(long triggerTime, boolean keepAlive) {
|
|
|
|
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
|
|
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
|
if (mPreviousHB != null) {
|
|
try {
|
|
logger.info(String.format("Cancel Previos HB: " + format.format(new Date(mPreviousHeartbeatTime))));
|
|
alarmManager.cancel(mPreviousHB);
|
|
mPreviousHB = null;
|
|
} catch (Exception ex) {
|
|
}
|
|
mPreviousHeartbeatTime = 0;
|
|
}
|
|
Intent alarmIntent = new Intent();
|
|
alarmIntent.setAction(ACTION_HEARTBEAT);
|
|
if (keepAlive) {
|
|
alarmIntent.putExtra("keepAlive", keepAlive);
|
|
}
|
|
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
|
|
|
|
Date dt = new Date(triggerTime);
|
|
|
|
if (keepAlive) {
|
|
logger.info(String.format("Register KeepAlive HB: " + format.format(dt)) + " MntnMode=" + (mMntnMode ? "1" : "0") + " QuickHb=" + (mQuickHbMode ? "1" : "0"));
|
|
} else {
|
|
logger.info(String.format("Register HB: " + format.format(dt)) + " MntnMode=" + (mMntnMode ? "1" : "0") + " QuickHb=" + (mQuickHbMode ? "1" : "0"));
|
|
}
|
|
mPreviousHB = pendingIntent;
|
|
mPreviousHeartbeatTime = triggerTime;
|
|
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
|
|
}
|
|
|
|
@Override
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
|
|
if (intent == null) {
|
|
stopForeground(true);
|
|
stopSelf();
|
|
return START_NOT_STICKY;
|
|
}
|
|
|
|
// if user starts the service
|
|
switch (intent.getAction()) {
|
|
case ACTION_START:
|
|
Log.d(TAG, "Received user starts foreground intent");
|
|
startForeground(NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
|
|
|
|
connect();
|
|
|
|
registerReceiver(mScreenaAtionReceiver, mScreenaAtionReceiver.getFilter());
|
|
|
|
String appPath = MicroPhotoContext.buildAppDir(this.getApplicationContext());
|
|
|
|
String cmdid = intent.getStringExtra("cmdid");
|
|
if (TextUtils.isEmpty(cmdid)) {
|
|
mCmdid = SysApi.getSerialNo(getApplicationContext());
|
|
} else {
|
|
mCmdid = cmdid;
|
|
}
|
|
|
|
logger.info("AppPath=" + appPath + " cmdid=" + cmdid);
|
|
|
|
// startMaster(false);
|
|
mHander.postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
startMpApp();
|
|
}
|
|
}, 5000);
|
|
|
|
registerHeartbeatTimer();
|
|
|
|
startMaster(false);
|
|
|
|
break;
|
|
case ACTION_STOP:
|
|
unregisterReceiver(mScreenaAtionReceiver);
|
|
|
|
stopForeground(true);
|
|
stopSelf();
|
|
break;
|
|
default:
|
|
stopForeground(true);
|
|
stopSelf();
|
|
}
|
|
|
|
return START_NOT_STICKY;
|
|
}
|
|
|
|
private void connect() {
|
|
// after 10 seconds its connected
|
|
mHander.postDelayed(
|
|
new Runnable() {
|
|
public void run() {
|
|
// Log.d(TAG, "Bluetooth Low Energy device is connected!!");
|
|
Toast.makeText(getApplicationContext(), "Connected!", Toast.LENGTH_SHORT).show();
|
|
mStateService = STATE_SERVICE.CONNECTED;
|
|
startForeground(NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
|
|
}
|
|
}, 10000);
|
|
|
|
}
|
|
|
|
private Notification prepareNotification() {
|
|
// handle build version above android oreo
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O &&
|
|
mNotificationManager.getNotificationChannel(FOREGROUND_CHANNEL_ID) == null) {
|
|
CharSequence name = getString(R.string.text_name_notification);
|
|
int importance = NotificationManager.IMPORTANCE_DEFAULT;
|
|
NotificationChannel channel = new NotificationChannel(FOREGROUND_CHANNEL_ID, name, importance);
|
|
channel.enableVibration(false);
|
|
mNotificationManager.createNotificationChannel(channel);
|
|
}
|
|
|
|
Intent notificationIntent = new Intent(this, MainActivity.class);
|
|
notificationIntent.setAction(ACTION_MAIN);
|
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
|
|
// if min sdk goes below honeycomb
|
|
/*if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
|
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
} else {
|
|
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
}*/
|
|
|
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
|
|
// make a stop intent
|
|
Intent stopIntent = new Intent(this, MpMasterService.class);
|
|
stopIntent.setAction(ACTION_STOP);
|
|
PendingIntent pendingStopIntent = PendingIntent.getService(this, 0, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.notification);
|
|
remoteViews.setOnClickPendingIntent(R.id.btn_stop, pendingStopIntent);
|
|
|
|
// if it is connected
|
|
switch (mStateService) {
|
|
case STATE_SERVICE.NOT_CONNECTED:
|
|
remoteViews.setTextViewText(R.id.tv_state, "DISCONNECTED");
|
|
break;
|
|
case STATE_SERVICE.CONNECTED:
|
|
remoteViews.setTextViewText(R.id.tv_state, "CONNECTED");
|
|
break;
|
|
}
|
|
|
|
// notification builder
|
|
NotificationCompat.Builder notificationBuilder;
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
|
notificationBuilder = new NotificationCompat.Builder(this, FOREGROUND_CHANNEL_ID);
|
|
} else {
|
|
notificationBuilder = new NotificationCompat.Builder(this);
|
|
}
|
|
notificationBuilder
|
|
.setContent(remoteViews)
|
|
.setSmallIcon(R.mipmap.ic_launcher)
|
|
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
|
.setOnlyAlertOnce(true)
|
|
.setOngoing(true)
|
|
.setAutoCancel(true)
|
|
.setContentIntent(pendingIntent);
|
|
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
|
|
notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
|
}
|
|
|
|
return notificationBuilder.build();
|
|
}
|
|
|
|
public String buildAppDir() {
|
|
|
|
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
|
|
if (!path.endsWith(File.separator)) {
|
|
path += File.separator;
|
|
}
|
|
path += getApplicationContext().getPackageName() + File.separator;
|
|
File pathFile = new File(path);
|
|
if (!pathFile.exists() && !pathFile.mkdirs()) {
|
|
return null;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
public boolean updateMntn(String url) {
|
|
if (TextUtils.isEmpty(url)) {
|
|
return false;
|
|
}
|
|
String path = buildAppDir();
|
|
path += "data/Master.json";
|
|
JSONObject jsonObject = JSONUtils.loadJson(path);
|
|
|
|
String oldUrl = null;
|
|
try {
|
|
oldUrl = jsonObject.getString("url");
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
if (TextUtils.equals(url, oldUrl)) {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
jsonObject.put("url", url);
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
|
|
return JSONUtils.saveJson(path, jsonObject);
|
|
}
|
|
|
|
public boolean updateTime(long timeInMillis) {
|
|
try {
|
|
SysApi.setSystemTime(getApplicationContext(), timeInMillis);
|
|
} catch (Exception ex) {
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static int getSignalLevel(int num) {
|
|
String result = getSystemProperty("vendor.ril.nw.signalstrength.lte." + Integer.toString(num));
|
|
if (TextUtils.isEmpty(result)) {
|
|
return 0;
|
|
}
|
|
String[] items = result.split(",");
|
|
int rsrp = -140;
|
|
|
|
if (items != null && items.length > 0) {
|
|
try {
|
|
rsrp = Integer.parseInt(items[0]);
|
|
} catch (Exception ex) {
|
|
}
|
|
}
|
|
return getSignalLevel(rsrp, num);
|
|
}
|
|
|
|
public static int getSignalLevel(long ss, int num) {
|
|
|
|
int signalLevel = 0;
|
|
if (ss >= -97) {
|
|
signalLevel = 4;
|
|
} else if (ss >= -107) {
|
|
signalLevel = 3;
|
|
} else if (ss >= -117) {
|
|
signalLevel = 2;
|
|
} else if (ss >= -125) {
|
|
signalLevel = 1;
|
|
} else if (ss >= -140) {
|
|
signalLevel = 0;
|
|
}
|
|
|
|
return signalLevel;
|
|
}
|
|
|
|
public void downloadAndInstall(final String url) {
|
|
|
|
final Context context = getApplicationContext();
|
|
final String tempPath = MicroPhotoContext.buildAppDir(context) + File.separator + "tmp";
|
|
File file = new File(tempPath);
|
|
file.mkdirs();
|
|
final String filePath = tempPath + File.separator + "mp.apk";
|
|
Thread th =new Thread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
|
|
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
|
|
PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "com.xinyingpower.microphoto:Upgrader");
|
|
|
|
FileDownloader fd = new FileDownloader();
|
|
fd.download(url, filePath);
|
|
|
|
SysApi.installApk(context, filePath, context.getPackageName(), true);
|
|
|
|
try {
|
|
wl.setReferenceCounted(false);
|
|
wl.release();
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
});
|
|
th.start();
|
|
}
|
|
|
|
public void reboot(final int rebootType) {
|
|
|
|
Runnable runnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (rebootType == 0) {
|
|
logger.warning("Recv REBOOT MpMst APP cmd");
|
|
Context context = MpMasterService.this.getApplicationContext();
|
|
MicroPhotoContext.restartApp(context, context.getPackageName());
|
|
|
|
} else {
|
|
logger.warning("Recv RESET cmd");
|
|
SysApi.reboot(MpMasterService.this.getApplicationContext());
|
|
}
|
|
}
|
|
};
|
|
mHander.postDelayed(runnable, 1000);
|
|
}
|
|
|
|
public int getActiveSlotIndex() {
|
|
Context context = getApplicationContext();
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
|
|
SubscriptionManager subscriptionManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
|
|
int activeSubId = SubscriptionManager.getActiveDataSubscriptionId();
|
|
int activeSlotIdx = SubscriptionManager.getSlotIndex(activeSubId);
|
|
return activeSlotIdx + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public void selectSimCard(int num) {
|
|
logger.info("Try to Switch To SimCard: " + Integer.toString(num));
|
|
|
|
Context context = getApplicationContext();
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
|
|
SubscriptionManager subscriptionManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
|
|
int activeSubId = SubscriptionManager.getActiveDataSubscriptionId();
|
|
int activeSlotIdx = SubscriptionManager.getSlotIndex(activeSubId);
|
|
if (activeSlotIdx == (num - 1)) {
|
|
logger.info("Active SimCard is already " + Integer.toString(num));
|
|
} else {
|
|
int subIds[] = subscriptionManager.getSubscriptionIds(num - 1);
|
|
if (subIds != null && subIds.length > 0) {
|
|
setDefaultDataSubId(subIds[0]);
|
|
logger.info("Switched To SimCard: " + Integer.toString(num));
|
|
} else {
|
|
logger.warning("NO available network for simcard " + Integer.toString(num));
|
|
}
|
|
}
|
|
} else {
|
|
SysApi.selectSimCard4Data(context, num);
|
|
}
|
|
}
|
|
|
|
private void setDefaultDataSubId(int subId) {
|
|
SubscriptionManager subscriptionManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
|
|
try {
|
|
|
|
Method method = subscriptionManager.getClass().getDeclaredMethod("setDefaultDataSubId", int.class);
|
|
try {
|
|
method.invoke(subscriptionManager, subId);
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
telephonyManager.setDataEnabled(true);
|
|
}
|
|
Method method1 = telephonyManager.getClass().getDeclaredMethod("setDataEnabled", boolean.class);
|
|
method1.invoke(telephonyManager, true);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private int getDefaultDataSubId() {
|
|
SubscriptionManager subscriptionManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
|
|
try {
|
|
Method method = subscriptionManager.getClass().getDeclaredMethod("getDefaultDataSubscriptionId");
|
|
return (int) method.invoke(subscriptionManager);
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public static void restartApp(Context context) {
|
|
Intent intent = new Intent(context, MainActivity.class);
|
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
context.startActivity(intent);
|
|
|
|
System.exit(0);
|
|
}
|
|
|
|
public native static int getInt(int cmd);
|
|
public native static int setInt(int cmd, int val);
|
|
public native static int[] getStats(long ts);
|
|
public native static String getSystemProperty(String key);
|
|
public native static void rebootDevice();
|
|
|
|
////////////////////////GPS////////////////////
|
|
|
|
} |