feat(network): 新增应用管控接口与广播兼容适配

- 新增 getIconControl 接口,按禁用/显示清单管理桌面图标
- 应用管控数据与在线禁用数据分开存储
- 新增 ReceiverCompat 统一处理 Android13+ 广播注册导出标志
- 异常捕获交回系统默认处理,移除主线程异常吞没逻辑
- 定位数据存储由 SPUtils 迁移至 MMKV
- 修复百度定位 SDK 构造异常及隐私合规声明
This commit is contained in:
2026-10-20 10:49:03 +08:00
parent fa729afb49
commit a85db9ad49
35 changed files with 1302 additions and 668 deletions

View File

@@ -13,24 +13,25 @@ static def releaseTime() {
android {
namespace "com.aoleyun.sn"
compileSdkVersion 29
compileSdkVersion 36
buildToolsVersion "36.0.0"
defaultConfig {
applicationId "com.aoleyun.sn"
versionCode 246
versionName "1.6.1013"
versionCode 250
versionName "1.6.1016"
//There are no CERT files because If the mini sdk version is 23+, the AGP will ignore the V1 scheme signature.
minSdkVersion 24
targetSdkVersion 29
targetSdkVersion 36
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
//选择要添加的对应 cpu 类型的 .so 库。
abiFilters 'arm64-v8a', 'armeabi-v7a'
// 还可以添加 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
// 还可以添加 'armeabi', 'x86', 'mips', 'mips64'
}
lintOptions {
@@ -322,9 +323,31 @@ android {
v1SigningEnabled true
v2SigningEnabled true
}
crosshatch {
storeFile file("keystore/AospCrosshatch.keystore")
storePassword "Fan1996"
keyAlias "AospCrosshatch"
keyPassword "Fan1996"
v1SigningEnabled true
v2SigningEnabled true
}
}
buildTypes {
CrosshatchDebug.initWith(debug)
CrosshatchDebug {
buildConfigField "String", "platform", '"AOSP"'
debuggable true
versionNameSuffix "_debug"
signingConfig signingConfigs.crosshatch
}
CrosshatchRelease.initWith(release)
CrosshatchRelease {
buildConfigField "String", "platform", '"AOSP"'
signingConfig signingConfigs.crosshatch
}
G122JDebug.initWith(debug)
G122JDebug {
@@ -884,7 +907,7 @@ dependencies {
//阿里云推送
implementation 'com.aliyun.ams:alicloud-android-push:3.8.0'
//百度地图
implementation 'com.baidu.lbsyun:BaiduMapSDK_Location:9.1.8'
implementation 'com.baidu.lbsyun:BaiduMapSDK_Location:9.7.0'
//MMKV
implementation 'com.tencent:mmkv-static:2.4.0'
//bugly

Binary file not shown.

View File

@@ -107,6 +107,14 @@
<!-- 如果您的应用需要后台定位权限,且有可能运行在Android Q设备上,并且设置了target>28,必须增加这个权限声明 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- ============ 适配 Android 13 / 14+(targetSdk 33+)新增要求 ============ -->
<!-- 前台服务:Android 14 起必须声明具体类型并申请对应权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<!-- 通知权限:前台服务通知展示(系统应用默认已授予) -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- 精确闹钟:Android 14 起 SCHEDULE_EXACT_ALARM 默认被拒绝,需声明 USE_EXACT_ALARM -->
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<application
android:name=".base.BaseApplication"
android:allowBackup="true"
@@ -205,10 +213,16 @@
android:name=".service.ShakeDetectionService"
android:enabled="true"
android:exported="true" />
<!-- Android 14 起前台服务必须声明 foregroundServiceType,否则 startForeground 会抛异常;
specialUse 不受 Android 15 的 6 小时限制与 BOOT_COMPLETED 启动限制影响 -->
<service
android:name=".service.main.MainService"
android:enabled="true"
android:exported="true">
android:exported="true"
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="设备管控常驻服务,用于策略同步与设备状态维护" />
<intent-filter android:priority="1000">
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="android.intent.action.SCREEN_ON" />

View File

@@ -34,11 +34,9 @@ import com.aoleyun.sn.utils.FlowInfo;
import com.aoleyun.sn.utils.GetFlowUtil;
import com.aoleyun.sn.utils.JgyUtils;
import com.aoleyun.sn.utils.NetworkUtils;
import com.aoleyun.sn.utils.SPUtils;
import com.aoleyun.sn.utils.ServiceAliveUtils;
import com.aoleyun.sn.utils.Utils;
import com.blankj.utilcode.util.PathUtils;
import com.google.gson.Gson;
import com.tencent.mmkv.MMKV;
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
@@ -182,7 +180,6 @@ public class SplashActivity extends AppCompatActivity {
// }
// ActivityManager mAm = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
// mAm.killBackgroundProcesses("com.android.documentsui");
Log.e(TAG, "debugTest: SPUtils all " + new Gson().toJson(SPUtils.getAll(this)));
// Utils.getPublicIP(this);
Utils.obtainWifiInfo(this);

View File

@@ -120,7 +120,9 @@ public class DebugActivity extends BaseMvvmActivity<DebugViewModel, ActivityDebu
.setIntent(shortcutInfoIntent)
.build();
//当添加快捷方式的确认弹框弹出来时,将被回调,自定义Receiver
PendingIntent shortcutCallbackIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, ShortcutReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent shortcutCallbackIntent = PendingIntent.getBroadcast(context, 0,
new Intent(context, ShortcutReceiver.class),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
ShortcutManagerCompat.requestPinShortcut(context, info,
shortcutCallbackIntent.getIntentSender());
}

View File

@@ -40,7 +40,7 @@ import com.aoleyun.sn.service.main.MainService;
import com.aoleyun.sn.utils.ApkUtils;
import com.aoleyun.sn.utils.GlideLoadUtils;
import com.aoleyun.sn.utils.JgyUtils;
import com.aoleyun.sn.utils.SPUtils;
import com.aoleyun.sn.utils.ReceiverCompat;
import com.aoleyun.sn.utils.SysSettingUtils;
import com.aoleyun.sn.utils.TimeUtils;
import com.aoleyun.sn.utils.Utils;
@@ -445,7 +445,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
} else {
setIv_head(head_img);
}
SPUtils.put(MainActivity.this, CommonConfig.ADMIN_ID, studentsInfo.getAdmin_id());
mMMKV.encode(CommonConfig.ADMIN_ID, studentsInfo.getAdmin_id());
}
});
mViewModel.mEBagCodeData.observe(this, new Observer<Boolean>() {
@@ -589,7 +589,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
super.onPause();
NetworkUtils.unregisterNetworkStatusChangedListener(this);
if (mUpdateReceiver != null) {
unregisterReceiver(mUpdateReceiver);
ReceiverCompat.unregisterReceiver(this, mUpdateReceiver);
}
}
@@ -637,7 +637,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
private void unregister() {
if (mPanelReceiver != null) {
unregisterReceiver(mPanelReceiver);
ReceiverCompat.unregisterReceiver(this, mPanelReceiver);
}
}
@@ -648,7 +648,8 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
IntentFilter filter = new IntentFilter();
filter.addAction(PANEL_SHOW);
filter.addAction(PANEL_HIDE);
registerReceiver(mPanelReceiver, filter);
//面板显隐广播由兄弟管控应用下发,需保持可被外部发送
ReceiverCompat.registerReceiver(this, mPanelReceiver, filter, ReceiverCompat.RECEIVER_EXPORTED);
}
public static final String PANEL_SHOW = "panel_show_key";
@@ -680,7 +681,7 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
}
IntentFilter filter = new IntentFilter();
filter.addAction(UPDATE_LOCKED_STATUS);
registerReceiver(mUpdateReceiver, filter);
ReceiverCompat.registerReceiver(this, mUpdateReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
private class UpdateReceiver extends BroadcastReceiver {

View File

@@ -15,6 +15,7 @@ import com.aoleyun.sn.adapter.AppUpdateAdapter;
import com.aoleyun.sn.base.mvvm.BaseMvvmActivity;
import com.aoleyun.sn.bean.AppUpdateInfo;
import com.aoleyun.sn.databinding.ActivityUpdateBinding;
import com.aoleyun.sn.utils.ReceiverCompat;
import java.util.List;
@@ -71,7 +72,7 @@ public class UpdateActivity extends BaseMvvmActivity<UpdateViewModel, ActivityUp
protected void onDestroy() {
super.onDestroy();
if (mNewAppReceiver != null) {
unregisterReceiver(mNewAppReceiver);
ReceiverCompat.unregisterReceiver(this, mNewAppReceiver);
}
}
@@ -84,7 +85,8 @@ public class UpdateActivity extends BaseMvvmActivity<UpdateViewModel, ActivityUp
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
registerReceiver(mNewAppReceiver, filter);
//Android13+ 必须显式声明导出标志,否则注册时会抛 SecurityException
ReceiverCompat.registerReceiver(this, mNewAppReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
private NewAppReceiver mNewAppReceiver;

View File

@@ -1,8 +1,6 @@
package com.aoleyun.sn.base;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.text.TextUtils;
import android.util.Log;
@@ -94,30 +92,27 @@ public class BaseApplication extends MultiDexApplication {
}
private void catchException() {
final Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
Log.e("捕获异常子线程:", Thread.currentThread().getName() +
"在:" + e.getStackTrace()[0].getClassName());
e.printStackTrace();
try {
Log.e("捕获异常:", Thread.currentThread().getName() +
"在:" + e.getStackTrace()[0].getClassName());
e.printStackTrace();
} catch (Exception ignored) {
}
//交回系统默认处理(弹崩溃提示并结束进程,xcrash/Bugly 仍能正常抓取堆栈)。
//原实现在主线程用 while (true) { Looper.loop(); } 吞掉异常,
//会使进程处于"半死"状态:Activity/Service 实际未创建,界面点开即闪退,
//并最终演变为 ANR,故移除该写法。
if (defaultHandler != null) {
defaultHandler.uncaughtException(t, e);
}
}
}
);
//下面是新增方法!
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
while (true) {
try {
Looper.loop(); //会先执行这个方法,然后在执行下面的异常捕获方法!
} catch (Exception e) {
Log.e("捕获异常主线程:", Thread.currentThread().getName() + "在:" + e.getStackTrace()[0].getClassName());
e.printStackTrace();
}
}
}
});
}
interface OnRegisterResult {

View File

@@ -0,0 +1,28 @@
package com.aoleyun.sn.bean;
import java.io.Serializable;
public class IconControlInfo implements Serializable {
private static final long serialVersionUID = 7845568828765632321L;
String app_package;
// 1 展示 0 隐藏
int control_type;
public int getControl_type() {
return control_type;
}
public void setControl_type(int control_type) {
this.control_type = control_type;
}
public String getApp_package() {
return app_package;
}
public void setApp_package(String app_package) {
this.app_package = app_package;
}
}

View File

@@ -189,6 +189,10 @@ public class CommonConfig {
public final static String AOLE_ACTION_RESTORE_FORBID_ON = "aole_restore_forbid_on";
public final static String AOLE_ACTION_DISABLE_APP = "aole_disable_app_list";
/*应用管控禁用列表(getIconControl 接口下发,与 AOLE_ACTION_DISABLE_APP 是两套独立数据)*/
public final static String AOLE_ACTION_HIDE_DESKTOP_ICON = "aole_hide_desktop_icon_list";
/*应用管控要求显示列表(getIconControl 接口下发,优先级高于本地默认禁用)*/
public final static String AOLE_ACTION_SHOW_DESKTOP_ICON = "aole_show_desktop_icon_list";
/*公共应用白名单*/
public final static String AOLE_PUBLIC_APP_INSTALL_WHITELIST = "aole_public_app_install_whitelist_set";
/*分组应用白名单*/

View File

@@ -54,9 +54,6 @@ public class ReceiverHandler extends Handler {
return sDefaultInstance;
}
// private SharedPreferences mDefaultSharedPreferences =
// MyApplication.getInstance().getDefaultSharedPreferences();
@Override
public void handleMessage(Message msg) {
int what = msg.what;
@@ -143,28 +140,6 @@ public class ReceiverHandler extends Handler {
// DebugLoggerUIServiceManager.getInstance().getService().dealWithBypassAction(intent);
}
/**
* Judge whether need to start up DebugLoggerUI service at boot time. If none log instance was
* set to start automatically when boot up, just remove this process to avoid confuse user
*/
// private boolean needStartLogAtBootTime() {
// boolean needStart = false;
// for (Integer logType : Utils.LOG_TYPE_SET) {
// if (logType == Utils.LOG_TYPE_MET) {
// continue;
// }
// if (Utils.VALUE_START_AUTOMATIC_ON == mDefaultSharedPreferences.getBoolean(
// Utils.KEY_START_AUTOMATIC_MAP.get(logType),
// Utils.DEFAULT_CONFIG_LOG_AUTO_START_MAP.get(logType))) {
// needStart = true;
// break;
// }
// }
// Utils.logd(TAG, "-->needStartLogAtBootTime(), needStart=" + needStart);
// return needStart;
// }
public class ServiceNullException extends Exception {
private static final long serialVersionUID = -430298482097527072L;
}

View File

@@ -103,7 +103,7 @@ public class ConnectManager {
}
long nowTime = System.currentTimeMillis();
long lastTime = mMMKV.decodeLong(key + "_time");
// long lastTime = (long) SPUtils.get(mContext, key, 0L);
// long lastTime = mMMKV.decodeLong(key, 0L);
Log.e(TAG, "isNeedConnect: key = " + key + "\ttime = " + lastTime);
long intervalTime = getConnectModeTime(connectMode);
//防止一分钟内重复请求

View File

@@ -6,7 +6,7 @@ import android.provider.Settings;
import android.util.Log;
import com.aoleyun.sn.bean.ElectronicFenceInfo;
import com.aoleyun.sn.utils.SPUtils;
import com.aoleyun.sn.comm.CommonConfig;
import com.baidu.location.BDAbstractLocationListener;
import com.baidu.location.BDLocation;
import com.baidu.location.LocationClient;
@@ -29,10 +29,13 @@ import com.baidu.trace.model.CoordType;
import com.baidu.trace.model.LatLng;
import com.baidu.trace.model.OnTraceListener;
import com.baidu.trace.model.PushMessage;
import com.tencent.mmkv.MMKV;
public class MapManager {
private String TAG = "MapManager";
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
@SuppressLint("StaticFieldLeak")
private static MapManager sInstance;
private Context mContext;
@@ -65,14 +68,21 @@ public class MapManager {
}
public void initMap() {
LocationClient.setAgreePrivacy(true);
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.ASSISTED_GPS_ENABLED, 1);
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+gps");
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+network");
mLocationClient = new LocationClient(mContext);
mLocationClient.setLocOption(getDefaultLocationClientOption());
mLocationClient.registerLocationListener(mListener);
mLocationClient.stop();
mLocationClient.start();
//百度定位 SDK 9.7.0 起 LocationClient 构造方法声明了 throws Exception
try {
mLocationClient = new LocationClient(mContext);
mLocationClient.setLocOption(getDefaultLocationClientOption());
mLocationClient.registerLocationListener(mListener);
mLocationClient.stop();
mLocationClient.start();
} catch (Exception e) {
Log.e(TAG, "initMap: LocationClient 创建失败: " + e.getMessage(), e);
}
}
public LocationClient getLocationClient() {
@@ -131,28 +141,28 @@ public class MapManager {
case BDLocation.TypeOffLineLocation:// 离线定位结果
Log.e(TAG, "onLocationChanged: " + "定位成功");
Log.e(TAG, "onLocationChanged: " + location.getAddrStr() + location.getLocationDescribe());
SPUtils.put(mContext, "MapAddress", location.getAddrStr() + location.getLocationDescribe());
SPUtils.put(mContext, "longitude", location.getLongitude());
SPUtils.put(mContext, "latitude", location.getLatitude());
SPUtils.put(mContext, "MapError", "-");
mMMKV.encode("MapAddress", location.getAddrStr() + location.getLocationDescribe());
mMMKV.encode("longitude", String.valueOf(location.getLongitude()));
mMMKV.encode("latitude", String.valueOf(location.getLatitude()));
mMMKV.encode("MapError", "-");
break;
case BDLocation.TypeServerError:
Log.e(TAG, "onReceiveLocation: " + "服务端网络定位失败");
SPUtils.put(mContext, "MapError", "服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com,会有人追查原因");
mMMKV.encode("MapError", "服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com,会有人追查原因");
break;
case BDLocation.TypeNetWorkException:
Log.e(TAG, "onReceiveLocation: " + "网络不同导致定位失败,请检查网络是否通畅");
SPUtils.put(mContext, "MapError", "网络不同导致定位失败,请检查网络是否通畅");
mMMKV.encode("MapError", "网络不同导致定位失败,请检查网络是否通畅");
break;
case BDLocation.TypeCriteriaException:
Log.e(TAG, "onReceiveLocation: " + "无法获取有效定位依据导致定位失败");
SPUtils.put(mContext, "MapError", "无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机");
mMMKV.encode("MapError", "无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机");
break;
default:
}
}
Log.e(TAG, "MapAddress: " + (String) SPUtils.get(mContext, "MapAddress", "-"));
Log.e(TAG, "MapError: " + (String) SPUtils.get(mContext, "MapError", "-"));
Log.e(TAG, "MapAddress: " + mMMKV.decodeString("MapAddress", "-"));
Log.e(TAG, "MapError: " + mMMKV.decodeString("MapError", "-"));
// Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-network");
// Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-gps");
// Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "");
@@ -214,7 +224,7 @@ public class MapManager {
}
}
Log.e(TAG, "onLocationChanged: " + "定位失败");
SPUtils.put(mContext, "MapError", sb);
mMMKV.encode("MapError", sb.toString());
Log.e(TAG, "onLocDiagnosticMessage: " + sb);
// logMsg(sb.toString(), tag);
}

View File

@@ -47,6 +47,7 @@ import com.aoleyun.sn.bean.DefaultApp;
import com.aoleyun.sn.bean.DeveloperBean;
import com.aoleyun.sn.bean.EBagCode;
import com.aoleyun.sn.bean.ForceDownloadData;
import com.aoleyun.sn.bean.IconControlInfo;
import com.aoleyun.sn.bean.LessonApp;
import com.aoleyun.sn.bean.LessonJson;
import com.aoleyun.sn.bean.LessonSetting;
@@ -99,7 +100,6 @@ import com.aoleyun.sn.utils.JgyUtils;
import com.aoleyun.sn.utils.MD5Util;
import com.aoleyun.sn.utils.NetworkUtils;
import com.aoleyun.sn.utils.PowerOffAlarmUtils;
import com.aoleyun.sn.utils.SPUtils;
import com.aoleyun.sn.utils.SysSettingUtils;
import com.aoleyun.sn.utils.TimeUtils;
import com.aoleyun.sn.utils.URLUtils;
@@ -132,6 +132,7 @@ import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
@@ -717,10 +718,15 @@ public class NetInterfaceManager {
return schedule(getSystemSettingApi().getBrowserBookmarks(HTTP_KEY, Utils.getSerial(mContext)));
}
@Deprecated
public Observable<BaseResponse> getDesktopIconObservable() {
return schedule(getSystemSettingApi().getDesktopIcon(HTTP_KEY, Utils.getSerial(mContext)));
}
public Observable<BaseResponse<List<IconControlInfo>>> getIconControlObservable() {
return schedule(getSystemSettingApi().getIconControl(HTTP_KEY, Utils.getSerial(mContext)));
}
public Observable<BaseResponse> getSystemSettingObservable() {
return withCache(schedule(getSystemSettingApi().getSystemSettings(HTTP_KEY, Utils.getSerial(mContext))),
UrlAddress.GET_FIRMWARE,
@@ -1428,57 +1434,165 @@ public class NetInterfaceManager {
});
}
public void getDesktopIcon(BehaviorSubject<ActivityEvent> lifecycle, onCompleteCallback callback) {
getDesktopIconObservable()
/**
* 应用 getIconControl 接口下发的应用管控清单。
* <p>
* 清单为空(接口失败或没有数据)表示接口没有任何管控:上次下发的禁用清单会被清空(应用恢复显示),
* 未被接口提及的应用恢复为本地默认禁用。
* <p>
* 注意:应用图标禁用(AOLE_ACTION_HIDE_DESKTOP_ICON)与 getSnAppAttr 的在线禁用
* (AOLE_ACTION_DISABLE_APP)是两套独立数据,分开存放。
*
* @param disablePkgs 需要禁用的应用(control_type = 0)
* @param showPkgs 需要显示的应用(control_type = 1)
*/
private void applyIconControl(List<String> disablePkgs, List<String> showPkgs) {
//旧的禁用清单
Set<String> oldHidePkgs = ApkUtils.getDesktopHidePkgs(mContext);
//本次要禁用的包名(含不同机型同功能应用的包名映射),持久化后 showAllApp 会跳过这些应用,避免图标被重新显示
Set<String> hidePkgs = expandAlias(disablePkgs);
//1、先保存接口数据:后续放开判断(isKeepDisableByOtherSource)要读到最新的显示清单,
// 否则接口要求显示的应用会被"本地默认禁用"挡住
String hideString = TextUtils.join(",", hidePkgs);
String showString = TextUtils.join(",", showPkgs);
Log.e("getIconControl", "applyIconControl: disable = " + hideString + ", show = " + showString);
Settings.Global.putString(mContext.getContentResolver(),
CommonConfig.AOLE_ACTION_HIDE_DESKTOP_ICON,
TextUtils.isEmpty(hideString) ? "null" : hideString);
Settings.Global.putString(mContext.getContentResolver(),
CommonConfig.AOLE_ACTION_SHOW_DESKTOP_ICON,
TextUtils.isEmpty(showString) ? "null" : showString);
//2、按清单禁用
for (String pkg : hidePkgs) {
JgyUtils.getInstance().hideApp(pkg);
}
//3、按清单显示(被其他管控来源要求禁用的除外)
for (String pack : showPkgs) {
showAppIfAllowed(pack);
}
//4、上次禁用、本次既没要求禁用也没要求显示的应用,恢复显示
for (String pkg : oldHidePkgs) {
if (hidePkgs.contains(pkg) || showPkgs.contains(pkg)) {
continue;
}
showAppIfAllowed(pkg);
}
//5、接口清单本次既没要求禁用也没要求显示的应用,恢复本地默认禁用
// (例如后台删除了某应用的管控记录,它应回到机型的默认禁用状态)
for (String pkg : ApkUtils.getEffectiveDefaultDisableApp(mContext)) {
if (hidePkgs.contains(pkg) || showPkgs.contains(pkg) || ApkUtils.isDisable(mContext, pkg)) {
continue;
}
Log.e("getIconControl", "applyIconControl: 恢复默认禁用: " + pkg);
JgyUtils.getInstance().hideApp(pkg);
}
}
/**
* 显示应用(被其他管控来源要求禁用的除外)
*
* @param pkg 包名
*/
private void showAppIfAllowed(String pkg) {
if (ApkUtils.isKeepDisableByOtherSource(mContext, pkg, ApkUtils.SOURCE_DESKTOP_HIDE)) {
Log.e("getIconControl", "showAppIfAllowed: 被其他管控来源禁用,保持禁用: " + pkg);
return;
}
Log.e("getIconControl", "showAppIfAllowed: show: " + pkg);
JgyUtils.getInstance().showApp(pkg);
}
/**
* 展开包名映射:不同机型的同功能应用包名不同,禁用时需一起处理
*
* @param pkgs 接口下发的包名
* @return 实际需要禁用的包名
*/
private Set<String> expandAlias(List<String> pkgs) {
Set<String> hidePkgs = new HashSet<>();
for (String pack : pkgs) {
if (TextUtils.isEmpty(pack)) {
continue;
}
hidePkgs.add(pack);
if ("com.mediatek.camera".equals(pack)) {
hidePkgs.add("com.android.camera2");
}
if ("com.android.mms".equals(pack)) {
hidePkgs.add("com.android.messaging");
}
}
return hidePkgs;
}
public void getIconControl(BehaviorSubject<ActivityEvent> lifecycle, onCompleteCallback callback) {
getIconControlObservable()
.observeOn(Schedulers.io())
.compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
.subscribe(getDesktopIconObserver(callback));
.subscribe(getIconControlObserver(callback));
}
public void getDesktopIcon(onCompleteCallback callback) {
getDesktopIconObservable()
public void getIconControl(BehaviorSubject<ActivityEvent> lifecycle) {
getIconControlObservable()
.observeOn(Schedulers.io())
.subscribe(getDesktopIconObserver(callback));
.compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
.subscribe(getIconControlObserver(null));
}
public void getDesktopIcon() {
getDesktopIconObservable()
public void getIconControl() {
getIconControlObservable()
.observeOn(Schedulers.io())
.subscribe(getDesktopIconObserver(null));
.subscribe(getIconControlObserver(null));
}
public Observer<BaseResponse> getDesktopIconObserver(onCompleteCallback callback) {
return new Observer<BaseResponse>() {
public Observer<BaseResponse<List<IconControlInfo>>> getIconControlObserver(onCompleteCallback callback) {
return new Observer<BaseResponse<List<IconControlInfo>>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e("getDesktopIcon", "onSubscribe: ");
Log.e("getIconControl", "onSubscribe: ");
}
@Override
public void onNext(@NonNull BaseResponse response) {
Log.e("getDesktopIcon", "onNext: " + response);
public void onNext(BaseResponse<List<IconControlInfo>> listBaseResponse) {
Log.e("getIconControl", "onNext: " + listBaseResponse);
//先把不在管控清单里的应用恢复显示
ApkUtils.showAllApp(mContext);
if (response.isSuccess()) {
String data = response.data.toString();
if (!TextUtils.isEmpty(data)) {
List<String> newList = Arrays.asList(data.split(","));//新的list
PackageManager pm = mContext.getPackageManager();
for (String pack : newList) {
try {
if ("com.mediatek.camera".equals(pack)) {
JgyUtils.getInstance().hideApp("com.android.camera2");
List<String> disablePkgs = new ArrayList<>();
List<String> showPkgs = new ArrayList<>();
if (listBaseResponse.isSuccess() && listBaseResponse.data != null) {
List<IconControlInfo> iconControlInfoList = listBaseResponse.data;
disablePkgs = iconControlInfoList.stream()
.filter(new Predicate<IconControlInfo>() {
@Override
public boolean test(IconControlInfo iconControlInfo) {
return iconControlInfo.getControl_type() == 0;
}
if ("com.android.mms".equals(pack)) {
JgyUtils.getInstance().hideApp("com.android.messaging");
})
.map(new Function<IconControlInfo, String>() {
@Override
public String apply(IconControlInfo iconControlInfo) {
return iconControlInfo.getApp_package();
}
JgyUtils.getInstance().hideApp(pack);
Log.e("getDesktopIcon", pack);
} catch (Exception e) {
Log.e("getDesktopIcon", "Exception: " + e.getMessage());
}
}
}
}).collect(Collectors.toList());
showPkgs = iconControlInfoList.stream()
.filter(new Predicate<IconControlInfo>() {
@Override
public boolean test(IconControlInfo iconControlInfo) {
return iconControlInfo.getControl_type() == 1;
}
})
.map(new Function<IconControlInfo, String>() {
@Override
public String apply(IconControlInfo iconControlInfo) {
return iconControlInfo.getApp_package();
}
}).collect(Collectors.toList());
}
Log.e("getIconControl", "onNext: disablePkgs = " + disablePkgs + ", showPkgs = " + showPkgs);
//清单为空(接口失败或没有数据)时同样按"接口清单为空"处理:
//上次接口禁用的应用恢复显示,未被接口提及的应用恢复为本地默认禁用
applyIconControl(disablePkgs, showPkgs);
if (JgyUtils.getInstance().isAiuduPlatform()) {
ApkUtils.HideAiUDuApp();
}
@@ -1486,13 +1600,17 @@ public class NetInterfaceManager {
@Override
public void onError(@NonNull Throwable e) {
Log.e("getDesktopIcon", "onError: " + e.getMessage());
onComplete();
Log.e("getIconControl", "onError: " + e.getMessage());
//接口异常同样视为没有数据,禁用本机默认禁用的应用
ApkUtils.hideDefaultDisableApp(mContext);
if (callback != null) {
callback.onComplete();
}
}
@Override
public void onComplete() {
Log.e("getDesktopIcon", "onComplete: ");
Log.e("getIconControl", "onComplete: ");
if (callback != null) {
callback.onComplete();
}
@@ -1618,18 +1736,19 @@ public class NetInterfaceManager {
}
private void setAppDisable(List<AppAttr> appAttrList) {
String appString = Settings.Global.getString(mContext.getContentResolver(), CommonConfig.AOLE_ACTION_DISABLE_APP);
if (!TextUtils.isEmpty(appString)) {
List<String> pkgs = new ArrayList<>(Arrays.asList(appString.split(",")));
for (String pkg : pkgs) {
JgyUtils.getInstance().showApp(pkg);
}
}
//注意:这里只处理 getSnAppAttr 接口下发的禁用清单(AOLE_ACTION_DISABLE_APP),
//与 getIconControl 接口下发的禁用清单(AOLE_ACTION_HIDE_DESKTOP_ICON)是两套独立数据
List<String> oldPkgs = ApkUtils.parsePkgList(
Settings.Global.getString(mContext.getContentResolver(), CommonConfig.AOLE_ACTION_DISABLE_APP));
if (appAttrList == null || appAttrList.size() == 0) {
//接口没有数据(失败或为空):只放开 AOLE_ACTION_DISABLE_APP 里记录的、
//且没有被其他管控来源要求禁用的应用
Log.e("getSnAppAttr", "onNext: appAttrList is empty");
showAppWithoutOtherSource(oldPkgs);
Settings.Global.putString(mContext.getContentResolver(), CommonConfig.AOLE_ACTION_DISABLE_APP, "null");
} else {
showAppWithoutOtherSource(oldPkgs);
for (AppAttr appAttr : appAttrList) {
if (appAttr.getIs_open() == 0) {
JgyUtils.getInstance().hideApp(appAttr.getApp_package());
@@ -1644,6 +1763,25 @@ public class NetInterfaceManager {
}
}
/**
* 放开在线禁用应用。
* <p>
* 只放开在线禁用清单(AOLE_ACTION_DISABLE_APP)中的应用,若该应用同时被其他管控来源
* (本地默认禁用、桌面图标隐藏、爱优读隐藏等)要求禁用,则保持禁用不做放开。
*
* @param pkgs 旧的在线的禁用包名
*/
private void showAppWithoutOtherSource(List<String> pkgs) {
for (String pkg : pkgs) {
if (ApkUtils.isKeepDisableByOtherSource(mContext, pkg, ApkUtils.SOURCE_ONLINE_DISABLE)) {
Log.e("getSnAppAttr", "showApp: 被其他管控来源禁用,保持禁用: " + pkg);
continue;
}
Log.e("getSnAppAttr", "showApp: " + pkg);
JgyUtils.getInstance().showApp(pkg);
}
}
public void setSystemSetting(BehaviorSubject<ActivityEvent> lifecycle, onCompleteCallback callback) {
getSystemSettingObservable()
.observeOn(Schedulers.io())
@@ -1674,7 +1812,7 @@ public class NetInterfaceManager {
@Override
public void onNext(@NonNull BaseResponse baseResponse) {
Log.e("setSystemSetting", "onNext: " + baseResponse);
SPUtils.put(mContext, CommonConfig.JGY_FIRST_CONNECT, 1);
mMMKV.encode(CommonConfig.JGY_FIRST_CONNECT, 1);
if (baseResponse.isSuccess()) {
String data = GsonUtils.toJSONString(baseResponse.data);
Log.e("setSystemSetting", "onNext: " + data);
@@ -1723,7 +1861,7 @@ public class NetInterfaceManager {
Settings.System.putString(mContext.getContentResolver(), CommonConfig.ACTIVATIONBEAN_CODE_KEY, eBagCode.getEbagCode());
}
String ebagCode = eBagCode.getEbagCode();
SPUtils.put(mContext, "ebagCode", ebagCode);
mMMKV.encode("ebagCode", ebagCode);
Settings.Global.putInt(mContext.getContentResolver(), CommonConfig.UIUI_ACTIVATION_KEY, 1);
JgyUtils.getInstance().checkLocalExternalFiles();
} else {
@@ -1864,11 +2002,11 @@ public class NetInterfaceManager {
private void setDefaultDesktop(String defaultApp) {
if (!TextUtils.isEmpty(defaultApp)) {
JgyUtils.getInstance().setDefaultDesktop(defaultApp);
// SPUtils.put(mContext, "default_launcher", defaultApp);
// mMMKV.encode(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, defaultApp);
} else {
//如果默认桌面不为空则设置为默认桌面
String default_launcher = mMMKV.decodeString(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, "");
// String default_launcher = (String) SPUtils.get(mContext, "default_launcher", "");
// String default_launcher = mMMKV.decodeString(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, "");
Log.e("getDefaultApp", "onNext: " + default_launcher);
if (TextUtils.isEmpty(default_launcher)) {
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.YXPD1Platform) {
@@ -1963,7 +2101,7 @@ public class NetInterfaceManager {
// int oldState = Settings.System.getInt(mContext.getContentResolver(), JGYActions.ACTION_QCH_UNLOCK_IPAD, JGYActions.FRAME_CODE_LOCKED);
// Log.e( "getLockedState", "qch_unlock_ipad: " + oldState);
//后台1是锁定,底层0是锁定
SPUtils.put(mContext, CommonConfig.JGY_FIRST_CONNECT, 1);
mMMKV.encode(CommonConfig.JGY_FIRST_CONNECT, 1);
if (locked == JGYActions.NET_CODE_LOCKED) {
// if (oldState == JGYActions.FRAME_CODE_UNLOCKED) {
// Log.e( "getLockedState", "onNext: " + "state changed , reset devices");
@@ -2764,21 +2902,21 @@ public class NetInterfaceManager {
}
String white_ip = browserData.getWhite_ip();
if (!TextUtils.isEmpty(white_ip)) {
SPUtils.put(mContext, "white_ip", white_ip);
mMMKV.encode("white_ip", white_ip);
} else {
SPUtils.put(mContext, "white_ip", " ");
mMMKV.encode("white_ip", " ");
}
String black_ip = browserData.getBlack_ip();
if (!TextUtils.isEmpty(black_ip)) {
SPUtils.put(mContext, "black_ip", black_ip);
mMMKV.encode("black_ip", black_ip);
} else {
SPUtils.put(mContext, "black_ip", " ");
mMMKV.encode("black_ip", " ");
}
} else {
Settings.System.putString(mContext.getContentResolver(), "DeselectBrowserArray", homePage);
Settings.System.putString(mContext.getContentResolver(), "qch_webblack_url", " ");
SPUtils.put(mContext, "white_ip", " ");
SPUtils.put(mContext, "black_ip", " ");
mMMKV.encode("white_ip", " ");
mMMKV.encode("black_ip", " ");
}
}
@@ -3714,15 +3852,14 @@ public class NetInterfaceManager {
Intent intent = new Intent(LogcatService.LOGCAT_START_ACTION);
PendingIntent startPendingIntent = PendingIntent.getBroadcast(mContext,
REQUEST_CODE, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
timestamp, startPendingIntent);
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
setExactAlarm(alarmManager, timestamp, startPendingIntent);
}
Intent intent = new Intent(LogcatService.LOGCAT_STOP_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
REQUEST_CODE, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
setExactAlarm(alarmManager,
timestamp + snRunLog.getDuration() * 1000, pendingIntent);
}
}
@@ -3740,6 +3877,23 @@ public class NetInterfaceManager {
});
}
/**
* 设置精确闹钟。
* <p>Android 14(targetSdk 33+)起 SCHEDULE_EXACT_ALARM 默认被拒绝,
* 权限不可用时会抛 SecurityException,这里降级为非精确闹钟,保证日志上传策略依然生效。
*/
private static void setExactAlarm(AlarmManager alarmManager, long triggerAtMillis, PendingIntent pendingIntent) {
if (alarmManager == null || pendingIntent == null) {
return;
}
try {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
} catch (SecurityException e) {
Log.e(TAG, "setExactAlarm: 精确闹钟不可用,降级为非精确闹钟: " + e.getMessage());
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
}
}
public static final String PublicIP = "PublicIP";
public static final String WHOIS_ADDR = "whois_ip_addr";

View File

@@ -110,7 +110,10 @@ public class UrlAddress {
/*小程序的管控设置*/
public static final String GET_SN_SETTING = "And/control/getSnSetting";
/*获取隐藏桌面图标*/
@Deprecated
public static final String GET_HIDE_DESKTOPICON = "Icon";
/*获取隐藏桌面图标 新版*/
public static final String GET_ICON_CONTROL = "icon/control";
/*获取系统设置*/
public static final String GET_FIRMWARE = "firmware/get";
/*获取时间管控*/

View File

@@ -5,6 +5,7 @@ import com.aoleyun.sn.bean.BaseResponse;
import com.aoleyun.sn.bean.BlacklistBean;
import com.aoleyun.sn.bean.BrowserBookmarks;
import com.aoleyun.sn.bean.BrowserData;
import com.aoleyun.sn.bean.IconControlInfo;
import com.aoleyun.sn.bean.PoweroffBean;
import com.aoleyun.sn.bean.SnSetting;
import com.aoleyun.sn.bean.SnTimeControl;
@@ -42,11 +43,18 @@ public interface SystemSettingApi {
@FormUrlEncoded
@POST(UrlAddress.GET_HIDE_DESKTOPICON)
@Deprecated
Observable<BaseResponse> getDesktopIcon(
@Field("key") String key,
@Field("sn") String sn
);
@GET(UrlAddress.GET_ICON_CONTROL)
Observable<BaseResponse<List<IconControlInfo>>> getIconControl(
@Query("key") String key,
@Query("sn") String sn
);
@GET(UrlAddress.GET_SN_TIME_CONTROL)
@Deprecated
Observable<BaseResponse<SnTimeControl>> getSnTimeControl(

View File

@@ -51,7 +51,7 @@ import com.aoleyun.sn.utils.CmdUtil;
import com.aoleyun.sn.utils.ForegroundAppUtil;
import com.aoleyun.sn.utils.JgyUtils;
import com.aoleyun.sn.utils.MySQLData;
import com.aoleyun.sn.utils.SPUtils;
import com.aoleyun.sn.utils.ReceiverCompat;
import com.aoleyun.sn.utils.ServiceAliveUtils;
import com.aoleyun.sn.utils.SysSettingUtils;
import com.aoleyun.sn.utils.TimeUtils;
@@ -336,7 +336,7 @@ public class PushManager {
@Override
public void run() {
NetInterfaceManager.getInstance().getAppLimit();
NetInterfaceManager.getInstance().getDesktopIcon();
NetInterfaceManager.getInstance().getIconControl();
NetInterfaceManager.getInstance().getDefaultDesktop();
NetInterfaceManager.getInstance().getAppAutoStartUpdateAndNet();
}
@@ -378,7 +378,7 @@ public class PushManager {
@Override
public void run() {
NetInterfaceManager.getInstance().getAppLimit();
NetInterfaceManager.getInstance().getDesktopIcon();
NetInterfaceManager.getInstance().getIconControl();
NetInterfaceManager.getInstance().getDefaultDesktop();
NetInterfaceManager.getInstance().getAppAutoStartUpdateAndNet();
}
@@ -421,7 +421,7 @@ public class PushManager {
new Handler(Looper.getMainLooper()).postDelayed(() -> {
//后台发送时数据库未改变,有时候可能获取到的数据时上一次的
Log.e(TAG, "run: HIDE_DESKTOP_ICON ");
NetInterfaceManager.getInstance().getDesktopIcon();
NetInterfaceManager.getInstance().getIconControl();
}, 2000);
break;
case DISABLE_APP_SLIDE:
@@ -457,28 +457,28 @@ public class PushManager {
case BDLocation.TypeOffLineLocation:// 离线定位结果
Log.e(TAG, "onLocationChanged: " + "定位成功");
Log.e(TAG, "onLocationChanged: " + location.getAddrStr() + location.getLocationDescribe());
SPUtils.put(mContext, "MapAddress", location.getAddrStr() + location.getLocationDescribe());
SPUtils.put(mContext, "longitude", location.getLongitude());
SPUtils.put(mContext, "latitude", location.getLatitude());
SPUtils.put(mContext, "MapError", "-");
mMMKV.encode("MapAddress", location.getAddrStr() + location.getLocationDescribe());
mMMKV.encode("longitude", String.valueOf(location.getLongitude()));
mMMKV.encode("latitude", String.valueOf(location.getLatitude()));
mMMKV.encode("MapError", "-");
break;
case BDLocation.TypeServerError:
Log.e(TAG, "onReceiveLocation: " + "服务端网络定位失败");
SPUtils.put(mContext, "MapError", "服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com,会有人追查原因");
mMMKV.encode("MapError", "服务端网络定位失败,可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com,会有人追查原因");
break;
case BDLocation.TypeNetWorkException:
Log.e(TAG, "onReceiveLocation: " + "网络不同导致定位失败,请检查网络是否通畅");
SPUtils.put(mContext, "MapError", "网络不同导致定位失败,请检查网络是否通畅");
mMMKV.encode("MapError", "网络不同导致定位失败,请检查网络是否通畅");
break;
case BDLocation.TypeCriteriaException:
Log.e(TAG, "onReceiveLocation: " + "无法获取有效定位依据导致定位失败");
SPUtils.put(mContext, "MapError", "无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机");
mMMKV.encode("MapError", "无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机");
break;
default:
}
}
Log.e(TAG, "MapAddress: " + SPUtils.get(mContext, "MapAddress", "-"));
Log.e(TAG, "MapError: " + SPUtils.get(mContext, "MapError", "-"));
Log.e(TAG, "MapAddress: " + mMMKV.decodeString("MapAddress", "-"));
Log.e(TAG, "MapError: " + mMMKV.decodeString("MapError", "-"));
locationClient.stop();
}
@@ -524,7 +524,7 @@ public class PushManager {
}
}
Log.e(TAG, "onLocationChanged: " + "定位失败");
SPUtils.put(mContext, "MapError", sb);
mMMKV.encode("MapError", sb.toString());
Log.e(TAG, "onLocDiagnosticMessage: " + sb);
}
});
@@ -783,7 +783,7 @@ public class PushManager {
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
NetInterfaceManager.getInstance().getDesktopIcon();
NetInterfaceManager.getInstance().getIconControl();
NetInterfaceManager.getInstance().getDefaultDesktop();
NetInterfaceManager.getInstance().getAppAutoStartUpdateAndNet();
}
@@ -890,7 +890,8 @@ public class PushManager {
if (Build.VERSION.SDK_INT >= 21) {
return ((BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE)).getIntProperty(4);
}
Intent intent = (new ContextWrapper(mContext)).registerReceiver(null, new IntentFilter("android.intent.action.BATTERY_CHANGED"));
Intent intent = ReceiverCompat.registerReceiver(new ContextWrapper(mContext), null,
new IntentFilter("android.intent.action.BATTERY_CHANGED"), ReceiverCompat.RECEIVER_NOT_EXPORTED);
return intent.getIntExtra("level", -1) * 100 / intent.getIntExtra("scale", -1);
}
@@ -1390,13 +1391,13 @@ public class PushManager {
String ebagCode = jsonObject.get("ebagCode").getAsString();
if (TextUtils.isEmpty(ebagCode)) {
Log.e(TAG, "setEbagCode: " + "ebagCode is empty");
SPUtils.put(mContext, "ebagCode", "");
mMMKV.encode("ebagCode", "");
if (JgyUtils.getInstance().checkAppPlatform() != JgyUtils.YXPD1Platform) {
Settings.System.putString(mContext.getContentResolver(), CommonConfig.ACTIVATIONBEAN_CODE_KEY, "");
Settings.Global.putInt(mContext.getContentResolver(), CommonConfig.UIUI_ACTIVATION_KEY, 0);
}
} else {
SPUtils.put(mContext, "ebagCode", ebagCode);
mMMKV.encode("ebagCode", ebagCode);
if (JgyUtils.getInstance().checkAppPlatform() != JgyUtils.YXPD1Platform) {
Settings.System.putString(mContext.getContentResolver(), CommonConfig.ACTIVATIONBEAN_CODE_KEY, ebagCode);
Settings.Global.putInt(mContext.getContentResolver(), CommonConfig.UIUI_ACTIVATION_KEY, 1);
@@ -1483,13 +1484,13 @@ public class PushManager {
if (is_log_type == 0) {
NetInterfaceManager.getInstance().uploadLogFile();
} else {
SPUtils.put(mContext, CommonConfig.UPLOAD_LOG_TYPE, 1);
mMMKV.encode(CommonConfig.UPLOAD_LOG_TYPE, 1);
}
}
private void getBatteryInfo(Context context) {
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent receiver = context.registerReceiver(null, filter);
Intent receiver = ReceiverCompat.registerReceiver(context, null, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
String jsonString = BatteryUtils.getBatteryJson(context, receiver).toString();
Gson gson = new Gson();
Type type = new TypeToken<BatteryInfo>() {

View File

@@ -11,7 +11,6 @@ import com.aoleyun.sn.rlog.LogDBManager;
import com.aoleyun.sn.utils.BootManager;
import com.aoleyun.sn.utils.JgyUtils;
import com.aoleyun.sn.utils.PowerOffAlarmUtils;
import com.aoleyun.sn.utils.SPUtils;
import com.aoleyun.sn.utils.TimeUtils;
import com.tencent.mmkv.MMKV;
@@ -80,9 +79,9 @@ public class BootReceiver extends BroadcastReceiver {
mMMKV.encode(ConnectManager.REBOOT_LAST_ONNECT_TIME, System.currentTimeMillis());
//设备重启后 AlarmManager 中的闹钟会被系统清空,重新安排定时关机
PowerOffAlarmUtils.schedule(context);
if ((int) SPUtils.get(context, CommonConfig.FIRST_STARTUP, 0) == 0) {
if (mMMKV.decodeInt(CommonConfig.FIRST_STARTUP, 0) == 0) {
LogDBManager.getInstance().creatRebootLog("首次启动", TimeUtils.transferLongToDate(System.currentTimeMillis()));
SPUtils.put(context, CommonConfig.FIRST_STARTUP, 1);
mMMKV.encode(CommonConfig.FIRST_STARTUP, 1);
} else {
LogDBManager.getInstance().creatRebootLog("设备重启", TimeUtils.transferLongToDate(System.currentTimeMillis()));
// Intent checkNetintent = new Intent(context, CheckNetActivity.class);

View File

@@ -119,7 +119,7 @@ public class NewAppReceiver extends BroadcastReceiver {
NetInterfaceManager.getInstance().getAppLimit();
// NetInterfaceManager.getInstance().getDefaultDesktop();
NetInterfaceManager.getInstance().getAppAutoStartUpdateAndNet();
NetInterfaceManager.getInstance().getDesktopIcon();
NetInterfaceManager.getInstance().getIconControl();
NetInterfaceManager.getInstance().sendInstalledAppInfo(new NetInterfaceManager.onCompleteCallback() {
@Override
public void onComplete() {
@@ -161,7 +161,7 @@ public class NewAppReceiver extends BroadcastReceiver {
Log.e("setLauncher", "onNext: " + pkg);
JgyUtils.getInstance().checkDefaultDesktop(pkg);
String oldDesktop = JgyUtils.getInstance().getDefaultDesktop();
// String oldDesktop = (String) SPUtils.get(mContext, "default_launcher", "");
// String oldDesktop = mMMKV.decodeString(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, "");
Log.e("setLauncher", "onNext: oldDesktop = " + oldDesktop);
if (pkg.equals(oldDesktop)) {
ApkUtils.openPackage(mContext, pkg);

View File

@@ -59,7 +59,7 @@ import com.aoleyun.sn.service.main.MainService;
import com.aoleyun.sn.utils.ApkUtils;
import com.aoleyun.sn.utils.CmdUtil;
import com.aoleyun.sn.utils.JgyUtils;
import com.aoleyun.sn.utils.SPUtils;
import com.aoleyun.sn.utils.ReceiverCompat;
import com.aoleyun.sn.utils.ServiceAliveUtils;
import com.aoleyun.sn.utils.TimeUtils;
import com.aoleyun.sn.utils.Utils;
@@ -72,6 +72,7 @@ import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import com.hjq.toast.Toaster;
import com.tencent.mmkv.MMKV;
import java.io.BufferedReader;
import java.io.File;
@@ -101,6 +102,8 @@ import okhttp3.ResponseBody;
public class GuardService extends Service {
private final static String TAG = "GuardService";
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
@@ -234,13 +237,13 @@ public class GuardService extends Service {
public void onDestroy() {
super.onDestroy();
if (null != mNewAppReceiver) {
unregisterReceiver(mNewAppReceiver);
ReceiverCompat.unregisterReceiver(this, mNewAppReceiver);
}
if (null != presentReceiver) {
unregisterReceiver(presentReceiver);
ReceiverCompat.unregisterReceiver(this, presentReceiver);
}
if (null != batteryReceiver) {
unregisterReceiver(batteryReceiver);
ReceiverCompat.unregisterReceiver(this, batteryReceiver);
}
}
@@ -255,7 +258,8 @@ public class GuardService extends Service {
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
// filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
registerReceiver(mNewAppReceiver, filter);
//Android13+ 必须显式声明导出标志,否则注册时会抛 SecurityException
ReceiverCompat.registerReceiver(this, mNewAppReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
private PresentReceiver presentReceiver;
@@ -275,7 +279,7 @@ public class GuardService extends Service {
filter.addAction("android.intent.action.FORCE_FACTORY_RESET");
filter.addAction(Intent.ACTION_SHUTDOWN);
filter.addAction(Intent.ACTION_REBOOT);
registerReceiver(presentReceiver, filter);
ReceiverCompat.registerReceiver(this, presentReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
public class PresentReceiver extends BroadcastReceiver {
@@ -338,8 +342,8 @@ public class GuardService extends Service {
break;
case Intent.ACTION_SHUTDOWN:
case Intent.ACTION_REBOOT:
int restart_count = (int) SPUtils.get(context, "restart_count", 0);
SPUtils.put(context, "restart_count", restart_count + 1);
int restart_count = mMMKV.decodeInt("restart_count", 0);
mMMKV.encode("restart_count", restart_count + 1);
// android.os.Process.killProcess(android.os.Process.myPid());
break;
case Intent.ACTION_FACTORY_RESET:
@@ -435,7 +439,7 @@ public class GuardService extends Service {
filter.addAction(Intent.ACTION_BATTERY_OKAY);
filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
registerReceiver(batteryReceiver, filter);
ReceiverCompat.registerReceiver(this, batteryReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
BatteryReceiver batteryReceiver;

View File

@@ -11,6 +11,7 @@ import android.util.Log;
import com.aoleyun.sn.bean.BaseResponse;
import com.aoleyun.sn.network.NetInterfaceManager;
import com.aoleyun.sn.utils.ReceiverCompat;
import com.aoleyun.sn.utils.Utils;
import java.io.BufferedReader;
@@ -106,7 +107,8 @@ public class LogcatService extends Service {
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(LOGCAT_START_ACTION);
filter.addAction(LOGCAT_STOP_ACTION);
registerReceiver(receiver, filter);
//Android13+ 必须显式声明导出标志,否则注册时会抛 SecurityException
ReceiverCompat.registerReceiver(this, receiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
Thread thread = null;
@@ -334,7 +336,7 @@ public class LogcatService extends Service {
public void onDestroy() {
super.onDestroy();
if (receiver != null) {
unregisterReceiver(receiver);
ReceiverCompat.unregisterReceiver(this, receiver);
}
}
}

View File

@@ -104,7 +104,7 @@ public class RemoteService extends Service {
// @Override
// public void setDefaultDesktop(String pkg) throws RemoteException {
// Log.e(TAG, "setDefaultDesktop: " + "pkg = " + pkg);
//// SPUtils.put(RemoteService.this, "default_launcher", pkg);
//// mMMKV.encode(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, pkg);
// mMMKV.encode(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, pkg);
// JgyUtils.getInstance().setDefaultDesktop(pkg);
// if (PackageNames.DESKTOP.equals(pkg)) {

View File

@@ -18,6 +18,7 @@ import androidx.annotation.Nullable;
import com.aoleyun.sn.BuildConfig;
import com.aoleyun.sn.KeepAliveConnection;
import com.aoleyun.sn.utils.JgyUtils;
import com.aoleyun.sn.utils.ReceiverCompat;
import com.aoleyun.sn.utils.ServiceAliveUtils;
import com.aoleyun.sn.utils.Utils;
import com.blankj.utilcode.util.NetworkUtils;
@@ -123,7 +124,8 @@ public class StepService extends Service implements NetworkUtils.OnNetworkStatus
filter.addAction(Intent.ACTION_REBOOT);
filter.addAction(Intent.ACTION_FACTORY_RESET);
filter.addAction(Intent.ACTION_MASTER_CLEAR);
registerReceiver(screenLockReceiver, filter);
//Android13+ 必须显式声明导出标志,否则注册时会抛 SecurityException
ReceiverCompat.registerReceiver(this, screenLockReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
private class ScreenLockReceiver extends BroadcastReceiver {
@@ -163,7 +165,7 @@ public class StepService extends Service implements NetworkUtils.OnNetworkStatus
NetworkUtils.unregisterNetworkStatusChangedListener(this);
closeConnect();
if (screenLockReceiver != null) {
unregisterReceiver(screenLockReceiver);
ReceiverCompat.unregisterReceiver(this, screenLockReceiver);
}
}

View File

@@ -60,7 +60,7 @@ import com.aoleyun.sn.utils.CacheUtils;
import com.aoleyun.sn.utils.ForegroundAppUtil;
import com.aoleyun.sn.utils.JgyUtils;
import com.aoleyun.sn.utils.PowerOffAlarmUtils;
import com.aoleyun.sn.utils.SPUtils;
import com.aoleyun.sn.utils.ReceiverCompat;
import com.aoleyun.sn.utils.SysSettingUtils;
import com.aoleyun.sn.utils.TimeUtils;
import com.aoleyun.sn.utils.Utils;
@@ -74,6 +74,8 @@ import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import io.reactivex.rxjava3.core.Observable;
@@ -310,35 +312,22 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
return mMainBinder;
}
/**
* 初始化任务后台线程(单线程串行,保证初始化顺序)
*/
private static final ExecutorService sInitExecutor = Executors.newSingleThreadExecutor();
@Override
public void onCreate() {
super.onCreate();
//前台服务必须在 startForegroundService() 之后 5 秒内调用 startForeground(),
//放在 onCreate 最前面执行,避免后续初始化耗时或抛异常导致前台服务超时(ANR)
startForegroundIfNeeded();
Log.e(TAG, "onCreate: ");
mServiceModel = new MainServiceModel(this);
mServiceModel.setLifecycle(getLifecycleSubject());
initLiveData();
if ("YX-T01".equalsIgnoreCase(Build.MODEL)) {
JgyUtils.getInstance().setTongyiAppTop();
}
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.YXPD1Platform) {
Settings.System.putString(getContentResolver(), CommonConfig.ACTIVATIONBEAN_CODE_KEY, Utils.getSerial(this));
if (ApkUtils.isAvailable(this, JgyUtils.JXW_LAUNCHER_PACKAGE_NAME)) {
Intent intent2 = new Intent(Intent.ACTION_VIEW);
intent2.setPackage(JgyUtils.JXW_LAUNCHER_PACKAGE_NAME);
intent2.setClassName(JgyUtils.JXW_LAUNCHER_PACKAGE_NAME, JgyUtils.JXW_LAUNCHER_CLASS_NAME);
try {
startService(intent2);//或者bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 都能启动
} catch (Exception e) {
Log.e(TAG, "onCreate: " + e.getMessage());
}
}
}
String launcherPkg = JgyUtils.getInstance().getDefaultDesktop();
Log.e(TAG, "onCreate: " + launcherPkg);
JgyUtils.getInstance().checkDefaultDesktop(launcherPkg);
TimeTask task = new TimeTask();
task.execute("ntp.aliyun.com");
@@ -357,34 +346,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
Log.e(TAG, "onCreate: device not reboot");
}
Settings.Global.putString(getContentResolver(), "AOLE_SERIAL", Utils.getSerial(MainService.this));
PackageManager pm = getPackageManager();
try {
Log.e(TAG, "initView: " + pm.getApplicationEnabledSetting("com.teclast.update"));
pm.setApplicationEnabledSetting("com.teclast.update", PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0);
Log.e(TAG, "onCreate: enable com.teclast.update");
} catch (Exception e) {
Log.e(TAG, "onCreate: setApplicationEnabledSetting = " + e.getMessage());
}
if ("V1.01_20230519".equalsIgnoreCase(Build.DISPLAY)) {
Log.e(TAG, "onCreate: enable");
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName("com.teclast.update", "com.teclast.update.GoogleOtaClient");
try {
startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "onCreate: startActivity = " + e.getMessage());
}
}
if (JgyUtils.getInstance().isAiuduPlatform()) {
// if (mMMKV.decodeInt(CommonConfig.DEVICES_FRIST_START, 0) == 0) {
// ApkUtils.openPackage(MainService.this, "com.books.wisdom");
// }
ApkUtils.HideAiUDuApp();
}
//Settings 写入、包管理查询与启停等耗时操作统一放到后台线程(见 initOnBackground)
// RoleManager mRm = getSystemService(RoleManager.class);
@@ -399,11 +361,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
// Log.e("RoleManager", "onRoleHoldersChanged: roleName = " + s + "userHandle = " + userHandle);
// }
// }, UserHandle.ALL);
//根据要求卸载这两个应用
if (JgyUtils.getInstance().getDeviceIsLocked()) {
ApkUtils.UninstallAPP(this, "com.joytv.live");
ApkUtils.UninstallAPP(this, "com.tencent.android.qqdownloader");
}
//根据要求卸载这两个应用(包管理耗时操作,移到后台线程执行,见 initOnBackground)
// Log.e(TAG, "onCreate: " + Build.MODEL);
// if ("K106".equals(Build.MODEL)) {
// if (ApkUtils.isAvailable(MainService.this, JXW_LAUNCHER)) {
@@ -411,12 +369,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
// }
// }
try {
JgyUtils.getInstance().hideApp("com.android.email");
JgyUtils.getInstance().hideApp("com.android.browser");
} catch (Exception e) {
Log.e(TAG, "onCreate: " + e.getMessage());
}
//隐藏系统邮件与浏览器(包管理耗时操作,移到后台线程执行,见 initOnBackground)
Observable.create(topAppSubscribe)
.throttleFirst(1, TimeUnit.MINUTES)
@@ -425,27 +378,12 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
.throttleFirst(3, TimeUnit.HOURS)
.subscribe(timeChangedObserver);
if ((int) SPUtils.get(this, CommonConfig.JGY_FIRST_CONNECT, 0) == 0) {
if (mMMKV.decodeInt(CommonConfig.JGY_FIRST_CONNECT, 0) == 0) {
Log.e(TAG, "onCreate: " + "Disable All Settings");
SysSettingUtils.setDisableSetting(this);
// mPresenter.getLockedState(CommonConfig.JGY_FIRST_CONNECT);
}
int oldVersionCode = (int) SPUtils.get(this, CommonConfig.SN_VERSIONCODE, 0);
if (oldVersionCode < BuildConfig.VERSION_CODE) {
Log.e(TAG, "onCreate: " + "upgrade successful");
try {
// new CacheUtils().cleanApplicationUserData(this, "com.aoleyun.os");
new CacheUtils().cleanApplicationUserData(this, "com.aoleyun.audos");
} catch (Exception e) {
Log.e(TAG, "onReceive: " + e.getMessage());
e.printStackTrace();
}
// mPresenter.checkAoleyunUpdate();
} else {
}
SPUtils.put(this, CommonConfig.SN_VERSIONCODE, BuildConfig.VERSION_CODE);
//版本升级后的用户数据清理为耗时 IO,移到后台线程执行(见 initOnBackground)
//根据本地保存的策略安排定时关机闹钟(服务重启、设备重启后重新计算)
PowerOffAlarmUtils.schedule(this);
@@ -454,25 +392,10 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
timeChangedStart.onstar(System.currentTimeMillis());
setStatusbar();
setFloatingWindow();
JgyUtils.getInstance().writeAppPackageList();
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.HL500Platform) {
mNotificationManagerCompat = NotificationManagerCompat.from(this);
createNotificationChannel();
sendSimpleNotification();
}
// aliyunPushInit();
IActivityManager activityManager = ActivityManagerNative.getDefault();
try {
activityManager.setActivityController(new AoleyunActivityController(), false);
} catch (RemoteException e) {
Log.e(TAG, "setActivityController: " + e.getMessage());
e.printStackTrace();
}
mMMKV.encode(CommonConfig.DEVICES_FRIST_START, 1);
Observable.create(killSubscribe)
.throttleLast(30, TimeUnit.SECONDS)
// .throttleLast(3, TimeUnit.SECONDS)
@@ -482,13 +405,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
.throttleFirst(5, TimeUnit.MINUTES)
.subscribe(networkObserver);
JgyUtils.getInstance().checkAoleyunApp();
String allowAppList = Settings.System.getString(getContentResolver(), CommonConfig.SEEWO_APP_SOURCE_INSTALL_WHITELIST);
if (TextUtils.isEmpty(allowAppList)) {
Settings.System.putString(getContentResolver(), CommonConfig.SEEWO_APP_SOURCE_INSTALL_WHITELIST, String.join(",", JgyUtils.DEFAULT_APP_SOURCE_WHITE_LIST));
}
//应用存在性检查与安装白名单初始化(Settings / 包管理读操作)移到后台线程(见 initOnBackground)
int reminder = mMMKV.decodeInt(CommonConfig.EYE_TO_SCREEN_DISTANCE_REMINDER, 0);
Log.e(TAG, "onCreate: reminder = " + reminder);
@@ -504,7 +421,156 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
startService(new Intent(this, ShakeDetectionService.class));
}
//以上是必须在主线程完成的工作(startForeground、LiveData、广播注册、悬浮窗等),
//其余 Settings、包管理、文件 IO 等耗时操作统一切到后台线程执行,
//避免服务创建阶段长时间阻塞主线程造成 Service ANR
runOnBackground(new Runnable() {
@Override
public void run() {
initOnBackground();
}
});
mMMKV.encode(CommonConfig.DEVICES_FRIST_START, 1);
}
/**
* 前台服务声明。
* <p>Android 14 起前台服务必须在清单中声明 foregroundServiceType(见 AndroidManifest),
* 并且 startForegroundService() 之后 5 秒内必须调用 startForeground(),
* 否则系统会抛出 ForegroundServiceDidNotStartInTimeException 并触发 ANR。
* 因此该方法在 onCreate 最前面执行,内部并做异常兜底。
*/
private void startForegroundIfNeeded() {
if (JgyUtils.getInstance().checkAppPlatform() != JgyUtils.HL500Platform) {
return;
}
try {
mNotificationManagerCompat = NotificationManagerCompat.from(this);
createNotificationChannel();
sendSimpleNotification();
} catch (Exception e) {
Log.e(TAG, "startForegroundIfNeeded: " + e.getMessage());
}
}
/**
* 后台线程执行的初始化,只包含 Settings 读写、包管理、文件 IO 等非 UI 操作。
*/
private void initOnBackground() {
try {
if (mMMKV.decodeInt(CommonConfig.DEVICES_FRIST_START, 0) == 0) {
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.DX11Platform) {
Settings.System.putInt(getContentResolver(), CommonConfig.AOLE_ACTION_DEVELOPER_OPTIONS, 1);
}
if (!checkAoleyunApp()) {
Log.e(TAG, "onStartCommand: " + "checkAoleyunApp");
// mPresenter.checkAoleyunUpdate();
}
try {
//本地默认禁用的应用统一在 ApkUtils.DEFAULT_DISABLE_APP_MAP 中按机型维护,
//showAllApp() 会跳过这些应用,避免被重新启用
ApkUtils.hideDefaultDisableApp(this);
} catch (Exception e) {
Log.e(TAG, "initOnBackground: " + e.getMessage());
}
}
if ("YX-T01".equalsIgnoreCase(Build.MODEL)) {
JgyUtils.getInstance().setTongyiAppTop();
}
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.YXPD1Platform) {
Settings.System.putString(getContentResolver(), CommonConfig.ACTIVATIONBEAN_CODE_KEY, Utils.getSerial(this));
if (ApkUtils.isAvailable(this, JgyUtils.JXW_LAUNCHER_PACKAGE_NAME)) {
Intent intent2 = new Intent(Intent.ACTION_VIEW);
intent2.setPackage(JgyUtils.JXW_LAUNCHER_PACKAGE_NAME);
intent2.setClassName(JgyUtils.JXW_LAUNCHER_PACKAGE_NAME, JgyUtils.JXW_LAUNCHER_CLASS_NAME);
try {
startService(intent2);//或者bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 都能启动
} catch (Exception e) {
Log.e(TAG, "initOnBackground: " + e.getMessage());
}
}
}
String launcherPkg = JgyUtils.getInstance().getDefaultDesktop();
Log.e(TAG, "initOnBackground: " + launcherPkg);
JgyUtils.getInstance().checkDefaultDesktop(launcherPkg);
Settings.Global.putString(getContentResolver(), "AOLE_SERIAL", Utils.getSerial(MainService.this));
PackageManager pm = getPackageManager();
try {
Log.e(TAG, "initOnBackground: " + pm.getApplicationEnabledSetting("com.teclast.update"));
pm.setApplicationEnabledSetting("com.teclast.update", PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0);
Log.e(TAG, "initOnBackground: enable com.teclast.update");
} catch (Exception e) {
Log.e(TAG, "initOnBackground: setApplicationEnabledSetting = " + e.getMessage());
}
if ("V1.01_20230519".equalsIgnoreCase(Build.DISPLAY)) {
Log.e(TAG, "initOnBackground: enable");
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName("com.teclast.update", "com.teclast.update.GoogleOtaClient");
try {
startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "initOnBackground: startActivity = " + e.getMessage());
}
}
if (JgyUtils.getInstance().isAiuduPlatform()) {
ApkUtils.HideAiUDuApp();
}
//根据要求卸载这两个应用
if (JgyUtils.getInstance().getDeviceIsLocked()) {
ApkUtils.UninstallAPP(this, "com.joytv.live");
ApkUtils.UninstallAPP(this, "com.tencent.android.qqdownloader");
}
int oldVersionCode = mMMKV.decodeInt(CommonConfig.SN_VERSIONCODE, 0);
if (oldVersionCode < BuildConfig.VERSION_CODE) {
Log.e(TAG, "initOnBackground: " + "upgrade successful");
try {
new CacheUtils().cleanApplicationUserData(this, "com.aoleyun.audos");
} catch (Exception e) {
Log.e(TAG, "initOnBackground: " + e.getMessage());
}
}
mMMKV.encode(CommonConfig.SN_VERSIONCODE, BuildConfig.VERSION_CODE);
JgyUtils.getInstance().writeAppPackageList();
IActivityManager activityManager = ActivityManagerNative.getDefault();
try {
activityManager.setActivityController(new AoleyunActivityController(), false);
} catch (RemoteException e) {
Log.e(TAG, "setActivityController: " + e.getMessage());
}
JgyUtils.getInstance().checkAoleyunApp();
String allowAppList = Settings.System.getString(getContentResolver(), CommonConfig.SEEWO_APP_SOURCE_INSTALL_WHITELIST);
if (TextUtils.isEmpty(allowAppList)) {
Settings.System.putString(getContentResolver(), CommonConfig.SEEWO_APP_SOURCE_INSTALL_WHITELIST, String.join(",", JgyUtils.DEFAULT_APP_SOURCE_WHITE_LIST));
}
} catch (Exception e) {
Log.e(TAG, "initOnBackground: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 在后台单线程中串行执行初始化任务
*/
private void runOnBackground(Runnable runnable) {
try {
sInitExecutor.execute(runnable);
} catch (Exception e) {
Log.e(TAG, "runOnBackground: " + e.getMessage());
}
}
private void initLiveData() {
@@ -544,7 +610,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
mServiceModel.setLogoImg();
mServiceModel.getAllAppList();
mServiceModel.getBrowserBookmarks();
mServiceModel.getDesktopIcon();
mServiceModel.getIconControl();
mServiceModel.getAllInfoAndSetting();
mServiceModel.getAppAutoStartUpdateAndNet();
mServiceModel.setAppinsideWeb();
@@ -594,23 +660,23 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
showFloatingWindow(tips);
}
screenlocked = true;
SPUtils.put(MainService.this, "is_screen_lock", true);
SPUtils.put(MainService.this, "screen_tips", tips);
mMMKV.encode("is_screen_lock", true);
mMMKV.encode("screen_tips", tips);
} else {
if (!timelocked) {
hideFloatingWindow();
}
screenlocked = false;
SPUtils.put(MainService.this, "is_screen_lock", false);
SPUtils.put(MainService.this, "screen_tips", "");
mMMKV.encode("is_screen_lock", false);
mMMKV.encode("screen_tips", "");
}
} else {
if (!timelocked) {
hideFloatingWindow();
}
screenlocked = false;
SPUtils.put(MainService.this, "is_screen_lock", false);
SPUtils.put(MainService.this, "screen_tips", "");
mMMKV.encode("is_screen_lock", false);
mMMKV.encode("screen_tips", "");
}
}
});
@@ -624,14 +690,20 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand: ");
JgyUtils.getInstance().wakeUpAoleyunAPP();
if (mMMKV.decodeInt(CommonConfig.DEVICES_FRIST_START, 0) == 0) {
if (!checkAoleyunApp()) {
Log.e(TAG, "onStartCommand: " + "checkAoleyunApp");
// mPresenter.checkAoleyunUpdate();
}
try {
JgyUtils.getInstance().wakeUpAoleyunAPP();
// checkUpdate 内部会做安装包 MD5 校验、文件读写与 APK 安装,属于耗时 IO,
// 必须放到子线程执行,否则会阻塞主线程导致 ANR,且安装异常会直接闪退
new Thread(() -> {
try {
NetInterfaceManager.getInstance().checkUpdate();
} catch (Throwable e) {
Log.e(TAG, "onStartCommand: checkUpdate 异常 " + e.getMessage(), e);
}
}, "check-update").start();
} catch (Throwable e) {
Log.e(TAG, "onStartCommand: 异常 " + e.getMessage(), e);
}
NetInterfaceManager.getInstance().checkUpdate();
return START_STICKY;
}
@@ -687,7 +759,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
private void setStatusbar() {
// StatusBarManager mStatusBarManager = (StatusBarManager) getApplicationContext().getSystemService(Context.STATUS_BAR_SERVICE);
// mStatusBarManager.disable(StatusBarManager.DISABLE_RECENT | StatusBarManager.DISABLE_HOME);
String status = (String) SPUtils.get(this, JGYActions.ACTION_STATUSBAR_STATUS, "");
String status = mMMKV.decodeString(JGYActions.ACTION_STATUSBAR_STATUS, "");
Log.e(TAG, "setStatusbar: String: " + status);
if (TextUtils.isEmpty(status)) {
Log.e(TAG, "setStatusbar: " + "status is NULL");
@@ -709,25 +781,25 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
private void unRegisterReceivers() {
if (null != lockScreenReceiver) {
unregisterReceiver(lockScreenReceiver);
ReceiverCompat.unregisterReceiver(this, lockScreenReceiver);
}
if (null != mTimeChangedReceiver) {
unregisterReceiver(mTimeChangedReceiver);
ReceiverCompat.unregisterReceiver(this, mTimeChangedReceiver);
}
if (null != mInstallReceiver) {
unregisterReceiver(mInstallReceiver);
ReceiverCompat.unregisterReceiver(this, mInstallReceiver);
}
if (null != mWifiReceiver) {
unregisterReceiver(mWifiReceiver);
ReceiverCompat.unregisterReceiver(this, mWifiReceiver);
}
if (null != mRefreshReceiver) {
unregisterReceiver(mRefreshReceiver);
ReceiverCompat.unregisterReceiver(this, mRefreshReceiver);
}
if (null != screenLockReceiver) {
unregisterReceiver(screenLockReceiver);
ReceiverCompat.unregisterReceiver(this, screenLockReceiver);
}
if (null != mDisplayReceiver) {
unregisterReceiver(mDisplayReceiver);
ReceiverCompat.unregisterReceiver(this, mDisplayReceiver);
}
}
@@ -747,7 +819,8 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mWifiReceiver, filter);
//Android13+ 必须显式声明导出标志,否则注册时会抛 SecurityException
ReceiverCompat.registerReceiver(this, mWifiReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
public class WifiReceiver extends BroadcastReceiver {
@@ -806,7 +879,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
}
IntentFilter filter = new IntentFilter();
filter.addAction(RefreshReceiver.REFRESH_RECEIVER_ACTION);
registerReceiver(mRefreshReceiver, filter);
ReceiverCompat.registerReceiver(this, mRefreshReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
public class RefreshReceiver extends BroadcastReceiver {
@@ -833,7 +906,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(screenLockReceiver, filter);
ReceiverCompat.registerReceiver(this, screenLockReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
private class ScreenLockReceiver extends BroadcastReceiver {
@@ -944,7 +1017,8 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(LockScreenReceiver.action_lock);
filter.addAction(LockScreenReceiver.action_unlock);
registerReceiver(lockScreenReceiver, filter);
//锁屏管控广播由兄弟管控应用下发,需保持可被外部发送
ReceiverCompat.registerReceiver(this, lockScreenReceiver, filter, ReceiverCompat.RECEIVER_EXPORTED);
}
}
@@ -1025,7 +1099,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(TimeChangedReceiver.ACTION_UPDATE);
registerReceiver(mTimeChangedReceiver, filter);
ReceiverCompat.registerReceiver(this, mTimeChangedReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
public class TimeChangedReceiver extends BroadcastReceiver {
@@ -1070,7 +1144,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
filter.addAction(NIGHT_DISPLAY_DISABLED);
filter.addAction(READ_MODEL_ENABLED);
filter.addAction(READ_MODEL_DISABLED);
registerReceiver(mDisplayReceiver, filter);
ReceiverCompat.registerReceiver(this, mDisplayReceiver, filter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
}
public static final String NIGHT_DISPLAY_ENABLED = "NIGHT_DISPLAY_ENABLED";
@@ -1108,7 +1182,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
private static final String UPLOAD_LOG_TIME = "23:00:00";
private void checkUploadLogTime() {
int is_log_type = (int) SPUtils.get(this, CommonConfig.UPLOAD_LOG_TYPE, 0);
int is_log_type = mMMKV.decodeInt(CommonConfig.UPLOAD_LOG_TYPE, 0);
if (is_log_type == 0) {
Log.e(TAG, "checkUploadLogTime: " + "is_log_type = " + is_log_type);
} else {
@@ -1197,7 +1271,9 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
mInstallReceiver = new InstallReceiver();
}
IntentFilter intentFilter = new IntentFilter(INSTALLAPK_ACTION);
registerReceiver(mInstallReceiver, intentFilter, INSTALLAPK_PREMISSIONS, null);
//安装广播由兄弟管控应用下发(受签名权限保护),需显式声明导出
ReceiverCompat.registerReceiver(this, mInstallReceiver, intentFilter,
ReceiverCompat.RECEIVER_EXPORTED, INSTALLAPK_PREMISSIONS);
}
private static final String INSTALLAPK_ACTION = "JGY_INSTALLAPK_ACTION";

View File

@@ -20,7 +20,6 @@ import com.aoleyun.sn.comm.CommonConfig;
import com.aoleyun.sn.comm.JGYActions;
import com.aoleyun.sn.network.NetInterfaceManager;
import com.aoleyun.sn.utils.JgyUtils;
import com.aoleyun.sn.utils.SPUtils;
import com.aoleyun.sn.utils.SysSettingUtils;
import com.aoleyun.sn.utils.URLUtils;
import com.aoleyun.sn.utils.Utils;
@@ -131,7 +130,7 @@ public class MainServiceModel extends ViewModel {
* 第一次连接
*/
public void getFirstConnect() {
int first = (int) SPUtils.get(mContext, CommonConfig.JGY_FIRST_CONNECT, 0);
int first = mMMKV.decodeInt(CommonConfig.JGY_FIRST_CONNECT, 0);
Log.e(TAG, "getFirstConnect: " + "first_connect = " + first);
if (first == 0) {
Log.e(TAG, "setDisableSetting: ");
@@ -377,13 +376,12 @@ public class MainServiceModel extends ViewModel {
new URLUtils(mContext).setBrowserBlackList();
}
public void getDesktopIcon() {
public void getIconControl() {
NetInterfaceManager.getInstance()
.getDesktopIcon(getLifecycle(), new NetInterfaceManager.onCompleteCallback() {
.getIconControl(getLifecycle(), new NetInterfaceManager.onCompleteCallback() {
@Override
public void onComplete() {
Log.e("getDesktopIcon", "onComplete: ");
// JgyUtils.getInstance().hideSystemAPP();
Log.e("getIconControl", "onComplete: ");
}
});
}

View File

@@ -46,9 +46,12 @@ import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -355,6 +358,49 @@ public class ApkUtils {
this.add("com.android.browser");
}};
/**
* 所有机型公共的默认禁用应用({@link #DEFAULT_DISABLE_APP_MAP} 的固定 key)
*/
public static final String DEFAULT_DISABLE_APP_KEY = "default";
/**
* 设备型号前缀({@link #DEFAULT_DISABLE_APP_MAP} 中以该前缀 + Build.MODEL 作为 key)
*/
public static final String DEFAULT_DISABLE_APP_MODEL_PREFIX = "MODEL:";
/**
* 本地默认禁用的应用(按机型区分,设备启动时由 MainService 禁用)。
* <p>
* {@link #showAllApp(Context)} 不会再把这些应用重新启用,避免与本地默认禁用、
* 后台在线下发的禁用互相覆盖。
* <p>
* key 支持两种写法,命中后会被叠加:
* <p>
* 1、平台:"default" 表示所有机型;其它按 build.gradle 中 buildConfigField 配置的 platform
* (即 {@link BuildConfig#platform}),如 "QualcommA"、"MT6765"、"MTKG11"、"展锐";
* <p>
* 2、设备型号:"MODEL:" 前缀 + {@link Build#MODEL},如 "MODEL:G1096JW"。
* <p>
* 需要给某个机型单独配置时,在这里增加对应 key 与包名即可。
*/
public static final Map<String, Set<String>> DEFAULT_DISABLE_APP_MAP = new HashMap<String, Set<String>>() {{
//所有机型默认禁用
put(DEFAULT_DISABLE_APP_KEY, new HashSet<String>() {{
this.add("com.android.email");
this.add("com.android.browser");
}});
put(JgyUtils.Qualcomm_TAG, new HashSet<String>() {{
this.add("org.chromium.chrome");
this.add("com.onyx.appmarket");
this.add("com.onyx.aiassistant");
this.add("com.onyx.android.production.test");
this.add("com.onyx.dict");
this.add("com.onyx.mail");
this.add("com.onyx.musicplayer");
this.add("com.onyx.voicerecorder");
}});
}};
/**
* 预置的可以卸载的系统应用,判断出来为第三方
*/
@@ -912,10 +958,15 @@ public class ApkUtils {
public static void installApp(Context context, String filePath) {
Log.e(TAG, "installApp: " + filePath);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
installAppatPie(context, filePath);
} else {
installApps(filePath);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
installAppatPie(context, filePath);
} else {
installApps(filePath);
}
} catch (Throwable e) {
// 兜底:安装入口不向调用方抛异常,避免主线程/服务线程因安装失败而闪退
Log.e(TAG, "installApp: 安装异常 " + e.getMessage(), e);
}
}
@@ -966,35 +1017,87 @@ public class ApkUtils {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static void installAppatPie(Context context, String apkFilePath) {
File file = new File(apkFilePath);
Log.e(TAG, "installAppatPie: 正在安装应用 " + apkFilePath);
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller
.SessionParams.MODE_FULL_INSTALL);
sessionParams.setSize(file.length());
int sessionId = createSession(packageInstaller, sessionParams);
if (sessionId != -1) {
boolean copySuccess = copyApkFile(packageInstaller, sessionId, apkFilePath);
if (copySuccess) {
Toaster.show("正在安装应用");
install(packageInstaller, sessionId, context);
try {
File file = new File(apkFilePath);
Log.e(TAG, "installAppatPie: 正在安装应用 " + apkFilePath);
if (!file.exists() || file.length() <= 0) {
Log.e(TAG, "installAppatPie: 安装包不存在或为空 " + apkFilePath);
return;
}
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller
.SessionParams.MODE_FULL_INSTALL);
sessionParams.setSize(file.length());
int sessionId = createSession(packageInstaller, sessionParams);
if (sessionId != -1) {
boolean copySuccess = copyApkFile(packageInstaller, sessionId, apkFilePath);
if (copySuccess) {
Toaster.show("正在安装应用");
install(packageInstaller, sessionId, context);
} else {
Log.e(TAG, "installAppatPie: 拷贝安装包失败,放弃本次会话");
abandonSession(packageInstaller, sessionId);
}
}
} catch (Throwable e) {
// 安装失败只记录日志,绝不向外抛出,否则会直接导致调用方(含主线程)进程闪退
Log.e(TAG, "installAppatPie: 安装异常 " + e.getMessage(), e);
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private static void install(PackageInstaller packageInstaller, int sessionId, Context context) {
PackageInstaller.Session session = null;
try {
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
Intent intent = new Intent(context, InstallResultReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
1, intent,
PendingIntent.FLAG_UPDATE_CURRENT
);
session.commit(pendingIntent.getIntentSender());
} catch (IOException e) {
e.printStackTrace();
session = packageInstaller.openSession(sessionId);
session.commit(createResultPendingIntent(context).getIntentSender());
} catch (Throwable e) {
Log.e(TAG, "install: 安装失败 " + e.getMessage(), e);
abandonSession(packageInstaller, sessionId);
} finally {
if (session != null) {
try {
session.close();
} catch (Throwable ignore) {
}
}
}
}
/**
* 创建 PackageInstaller 的安装结果回调 PendingIntent。
* <p>
* Android 14(targetSdk 34+)起,系统会向该 PendingIntent 回填安装结果,
* 因此必须使用 FLAG_MUTABLE,否则 commit() 会抛出
* IllegalArgumentException: The commit() status receiver should come from a mutable PendingIntent,
* 该异常在旧代码中会直接冒泡到主线程造成进程闪退。
*/
private static PendingIntent createResultPendingIntent(Context context) {
Intent intent = new Intent(context, InstallResultReceiver.class);
intent.setPackage(context.getPackageName());
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
if (Build.VERSION.SDK_INT >= ANDROID_12) {
flags |= FLAG_MUTABLE;
}
return PendingIntent.getBroadcast(context, 1, intent, flags);
}
/**
* Android 12(API 31)。编译使用的 framework.jar 为设备提取版,不含 API 31 新增符号,
* 因此这里使用字面量常量代替 {@code Build.VERSION_CODES.S} 与 {@code PendingIntent.FLAG_MUTABLE}。
*/
private static final int ANDROID_12 = 31;
/**
* PendingIntent.FLAG_MUTABLE(API 31 新增,值为 1 << 25)。
*/
private static final int FLAG_MUTABLE = 1 << 25;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private static void abandonSession(PackageInstaller packageInstaller, int sessionId) {
try {
packageInstaller.abandonSession(sessionId);
} catch (Throwable e) {
Log.e(TAG, "abandonSession: " + e.getMessage());
}
}
@@ -1150,11 +1253,14 @@ public class ApkUtils {
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static void uninstall(Context context, String packageName) {
Intent broadcastIntent = new Intent(context, InstallResultReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1,
broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
packageInstaller.uninstall(packageName, pendingIntent.getIntentSender());
try {
// 卸载结果同样由系统回填到 PendingIntent 中,Android 14+ 必须为可变(Mutable)
PendingIntent pendingIntent = createResultPendingIntent(context);
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
packageInstaller.uninstall(packageName, pendingIntent.getIntentSender());
} catch (Throwable e) {
Log.e(TAG, "uninstall: 卸载失败 " + packageName + " " + e.getMessage(), e);
}
}
public static boolean pmUninstall(String packageName) {
@@ -1242,23 +1348,35 @@ public class ApkUtils {
public static void showAllApp(Context context) {
PackageManager pm = context.getPackageManager();
// 查询所有已经安装的应用程序
List<PackageInfo> packages = pm.getInstalledPackages(PackageManager.COMPONENT_ENABLED_STATE_ENABLED | PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
//本地默认禁用 + getSnAppAttr 在线禁用 + getIconControl 应用管控禁用,
//这三类应用要保持禁用状态,不能被重新启用
Set<String> keepDisablePkgs = getKeepDisablePkgs(context);
Log.e(TAG, "showAllApp: keepDisablePkgs = " + keepDisablePkgs);
// 查询所有已经安装的应用程序(原来传的是 3,等价于 GET_ACTIVITIES|GET_RECEIVERS,会多加载无用元数据)
List<PackageInfo> packages = pm.getInstalledPackages(0);
boolean isAiuduPlatform = JgyUtils.getInstance().isAiuduPlatform();
for (PackageInfo packageInfo : packages) {
String pkg = packageInfo.packageName;
Log.e(TAG, "showAllApp: " + pkg + " disable = " + (pm.getApplicationEnabledSetting(pkg) == PackageManager.COMPONENT_ENABLED_STATE_DISABLED));
if (canremove_systemapp.contains(pkg)
&& !show_canremove_systemapp.contains(pkg)) {
Log.e(TAG, "showAllApp: continue: " + pkg);
continue;
}
if (JgyUtils.getInstance().isAiuduPlatform()) {
if (AiudHideApp.contains(pkg)) {
continue;
}
if (isAiuduPlatform && AiudHideApp.contains(pkg)) {
continue;
}
if (keepDisablePkgs.contains(pkg)) {
Log.e(TAG, "showAllApp: keep disable: " + pkg);
continue;
}
try {
Log.i(TAG, "showAllApp: show: " + pkg);
int state = pm.getApplicationEnabledSetting(pkg);
//已经是启用状态,无需重复设置(避免每次启动都做一遍无用的跨进程调用)
if (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
continue;
}
Log.e(TAG, "showAllApp: show: " + pkg + ", state = " + state);
pm.setApplicationEnabledSetting(pkg, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0);
} catch (Exception e) {
Log.e(TAG, "showAllApp: " + e.getMessage());
@@ -1266,6 +1384,193 @@ public class ApkUtils {
}
}
/**
* 获取需要保持禁用状态的应用,包含:
* <p>
* 1、当前生效的本地默认禁用应用({@link #getEffectiveDefaultDisableApp(Context)});
* <p>
* 2、getSnAppAttr 接口下发的在线禁用应用;
* <p>
* 3、getIconControl 接口下发的禁用应用。
*
* @param context 上下文
* @return 不允许被 {@link #showAllApp(Context)} 重新启用的包名集合
*/
public static Set<String> getKeepDisablePkgs(Context context) {
Set<String> pkgs = new HashSet<>(getEffectiveDefaultDisableApp(context));
pkgs.addAll(getOnlineDisablePkgs(context));
pkgs.addAll(getDesktopHidePkgs(context));
return pkgs;
}
/**
* 获取当前机型本地默认禁用的应用。
* <p>
* 结果由 {@link #DEFAULT_DISABLE_APP_MAP} 决定,按"所有机型 + 平台 + 设备型号"三级叠加,
* 见 {@link #DEFAULT_DISABLE_APP_MAP} 的说明。
*
* @param context 上下文
* @return 当前机型需要默认禁用的包名集合
*/
public static Set<String> getDefaultDisableApp(Context context) {
String platform = BuildConfig.platform;
String model = Build.MODEL;
Set<String> pkgs = new HashSet<>();
//所有机型公共的默认禁用应用
pkgs.addAll(getDefaultDisableAppByKey(DEFAULT_DISABLE_APP_KEY));
//按平台区分的默认禁用应用
pkgs.addAll(getDefaultDisableAppByKey(platform));
//按设备型号区分的默认禁用应用
pkgs.addAll(getDefaultDisableAppByKey(DEFAULT_DISABLE_APP_MODEL_PREFIX + model));
Log.e(TAG, "getDefaultDisableApp: platform = " + platform + ", model = " + model + ", pkgs = " + pkgs);
return pkgs;
}
/**
* 按 key 取默认禁用应用清单
*
* @param key {@link #DEFAULT_DISABLE_APP_MAP} 的 key
* @return 对应的包名集合,没有配置时返回空集合
*/
private static Set<String> getDefaultDisableAppByKey(String key) {
Set<String> pkgs = DEFAULT_DISABLE_APP_MAP.get(key);
return pkgs == null ? Collections.emptySet() : pkgs;
}
/**
* 读取 getSnAppAttr 接口下发的在线禁用清单
*
* @param context 上下文
* @return 在线禁用的包名集合
*/
public static Set<String> getOnlineDisablePkgs(Context context) {
String appString = Settings.Global.getString(context.getContentResolver(), CommonConfig.AOLE_ACTION_DISABLE_APP);
Log.e(TAG, "getOnlineDisablePkgs: " + appString);
return new HashSet<>(parsePkgList(appString));
}
/**
* 读取 getIconControl 接口下发的禁用应用清单
*
* @param context 上下文
* @return 被禁用的包名集合
*/
public static Set<String> getDesktopHidePkgs(Context context) {
String appString = Settings.Global.getString(context.getContentResolver(), CommonConfig.AOLE_ACTION_HIDE_DESKTOP_ICON);
Log.e(TAG, "getDesktopHidePkgs: " + appString);
return new HashSet<>(parsePkgList(appString));
}
/**
* 在线禁用来源(getSnAppAttr 接口下发)
*/
public static final int SOURCE_ONLINE_DISABLE = 1;
/**
* 应用管控禁用来源(getIconControl 接口下发)
*/
public static final int SOURCE_DESKTOP_HIDE = 1 << 1;
/**
* 读取 getIconControl 接口要求显示(control_type = 1)的应用清单
*
* @param context 上下文
* @return 接口要求显示的包名集合
*/
public static Set<String> getShowAppPkgs(Context context) {
String appString = Settings.Global.getString(context.getContentResolver(), CommonConfig.AOLE_ACTION_SHOW_DESKTOP_ICON);
Log.e(TAG, "getShowAppPkgs: " + appString);
return new HashSet<>(parsePkgList(appString));
}
/**
* 获取当前生效的默认禁用应用。
* <p>
* = 机型配置的默认禁用({@link #getDefaultDisableApp(Context)})− getIconControl 接口明确要求显示的应用,
* 即后台下发的"显示"优先级高于本地默认禁用。
*
* @param context 上下文
* @return 当前生效的默认禁用包名集合
*/
public static Set<String> getEffectiveDefaultDisableApp(Context context) {
Set<String> pkgs = new HashSet<>(getDefaultDisableApp(context));
pkgs.removeAll(getShowAppPkgs(context));
return pkgs;
}
/**
* 应用是否被"其他管控来源"要求保持禁用。
* <p>
* 用于放开某个来源的禁用清单时,避免把其他来源(本地默认禁用、爱优读隐藏、预置可卸载应用,
* 以及未排除的在线禁用/应用管控禁用)要求禁用的应用一起放开。
*
* @param context 上下文
* @param pkg 包名
* @param excludeSources 需要排除的来源({@link #SOURCE_ONLINE_DISABLE}、
* {@link #SOURCE_DESKTOP_HIDE} 按位或),本人正在放开的来源需要排除
* @return true:该应用还被其他来源管控,不能放开
*/
public static boolean isKeepDisableByOtherSource(Context context, String pkg, int excludeSources) {
if (TextUtils.isEmpty(pkg)) {
return false;
}
if (getEffectiveDefaultDisableApp(context).contains(pkg)) {
return true;
}
if (canremove_systemapp.contains(pkg) && !show_canremove_systemapp.contains(pkg)) {
return true;
}
if (JgyUtils.getInstance().isAiuduPlatform() && AiudHideApp.contains(pkg)) {
return true;
}
if ((excludeSources & SOURCE_ONLINE_DISABLE) == 0 && getOnlineDisablePkgs(context).contains(pkg)) {
return true;
}
return (excludeSources & SOURCE_DESKTOP_HIDE) == 0 && getDesktopHidePkgs(context).contains(pkg);
}
/**
* 禁用当前生效的默认禁用应用(已禁用的跳过,被接口要求显示的不禁用)
*
* @param context 上下文
*/
public static void hideDefaultDisableApp(Context context) {
for (String pkg : getEffectiveDefaultDisableApp(context)) {
if (isDisable(context, pkg)) {
//已经是禁用状态(或未安装),无需重复禁用
continue;
}
Log.e(TAG, "hideDefaultDisableApp: hide: " + pkg);
JgyUtils.getInstance().hideApp(pkg);
}
}
/**
* 解析 Settings.Global 中以逗号分隔的包名清单
*
* @param appString 清单字符串
* @return 包名列表(已过滤空值、中括号以及后台无数据时写入的"null")
*/
public static List<String> parsePkgList(String appString) {
List<String> pkgs = new ArrayList<>();
if (TextUtils.isEmpty(appString)) {
return pkgs;
}
try {
for (String pkg : appString.split(",")) {
pkg = pkg.replace("[", "").replace("]", "").trim();
if (TextUtils.isEmpty(pkg) || "null".equalsIgnoreCase(pkg)) {
continue;
}
if (!pkgs.contains(pkg)) {
pkgs.add(pkg);
}
}
} catch (Exception e) {
Log.e(TAG, "parsePkgList: " + e.getMessage());
}
return pkgs;
}
public static void hideSystemSettingAPP(Context context, String pkage) {
int hide = 0;
//后台0显示,1隐藏

View File

@@ -42,11 +42,13 @@ public class InstallResultReceiver extends BroadcastReceiver {
// while (iterator.hasNext()) {
// Log.d("KEY", iterator.next());
// }
String STATUS = intent.getStringExtra(PackageInstaller.EXTRA_STATUS);
//STATUS / SESSION_ID / LEGACY_STATUS 都是 int 类型 extra,
//使用 getStringExtra 读取会抛 ClassCastException(被 Bundle 吞掉并打印警告)
int STATUS = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
String PACKAGE_NAME = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME);
String SESSION_ID = intent.getStringExtra(PackageInstaller.EXTRA_SESSION_ID);
int SESSION_ID = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
String STATUS_MESSAGE = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
String LEGACY_STATUS = intent.getStringExtra("android.content.pm.extra.LEGACY_STATUS");
int LEGACY_STATUS = intent.getIntExtra("android.content.pm.extra.LEGACY_STATUS", -1);
// Log.e("fht", STATUS);
// Log.e("fht", PACKAGE_NAME);
// Log.e("fht", SESSION_ID);

View File

@@ -30,7 +30,9 @@ import android.os.BatteryManager;
import android.os.Build;
import android.os.Debug;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -515,7 +517,8 @@ public class JgyUtils {
BatteryManager batteryManager = (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE);
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(mContext).registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Intent intent = ReceiverCompat.registerReceiver(new ContextWrapper(mContext), null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED), ReceiverCompat.RECEIVER_NOT_EXPORTED);
return (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
@@ -1088,7 +1091,7 @@ public class JgyUtils {
public void onBootSendNetwork() {
if (!MTK11Tag.equals(getAppPlatform())) {
String oldBlackListString = (String) SPUtils.get(mContext, CommonConfig.AOLE_ACTION_NETWORK_DISALLOW, "");
String oldBlackListString = mMMKV.decodeString(CommonConfig.AOLE_ACTION_NETWORK_DISALLOW, "");
HashSet<String> oldBlackListSet = new HashSet<>(Arrays.asList(oldBlackListString.split(",")));
Log.e(TAG, "setAppNetwork: oldBlackListSet: " + oldBlackListSet);
oldBlackListSet.removeIf(new Predicate<String>() {
@@ -1318,7 +1321,7 @@ public class JgyUtils {
private List<TTAppground> getOldWhitelist() {
String whiteListString = (String) SPUtils.get(mContext, JGY_APPINSIDE_WHITELIST, "");
String whiteListString = mMMKV.decodeString(JGY_APPINSIDE_WHITELIST, "");
Log.e(TAG, "getOldWhitelist: " + whiteListString);
Gson gson = new Gson();
Type listType = new TypeToken<List<TTAppground>>() {
@@ -1349,7 +1352,7 @@ public class JgyUtils {
}
});
String data = new Gson().toJson(appgrounds);
SPUtils.put(mContext, JGY_APPINSIDE_WHITELIST, data);
mMMKV.encode(JGY_APPINSIDE_WHITELIST, data);
}
public void onBootSetAppInsideWeb() {
@@ -1507,7 +1510,7 @@ public class JgyUtils {
if (locked == JGYActions.FRAME_CODE_LOCKED) {
SysSettingUtils.setDisableSetting(mContext);
}
SPUtils.put(mContext, JGYActions.ACTION_STATUSBAR_STATUS, "");
mMMKV.encode(JGYActions.ACTION_STATUSBAR_STATUS, "");
} else {
if (getAppPlatform().equals(JgyUtils.ZR6016Tag)) {
Log.e(TAG, "SettingSysData: ZR6016Tag");
@@ -2246,6 +2249,7 @@ public class JgyUtils {
/**
* @param state 1打开 0关闭
* dx11 state为1关闭,为0打开
*/
public void setDeveloperOptions(int state) {
Log.e(TAG, "setDeveloperOptions: state = " + state);
@@ -2428,11 +2432,11 @@ public class JgyUtils {
@Deprecated
public void cleanLauncherCache() {
Log.e(TAG, "cleanLauncherCache: Start");
int cleaned = (int) SPUtils.get(mContext, "fristcleanLauncherCache", 0);
int cleaned = mMMKV.decodeInt("fristcleanLauncherCache", 0);
if (cleaned == 0) {
try {
new CacheUtils().cleanApplicationUserData(mContext, "com.android.launcher3");
SPUtils.put(mContext, "fristcleanLauncherCache", 1);
mMMKV.encode("fristcleanLauncherCache", 1);
Log.e(TAG, "cleanLauncherCache: end");
} catch (Exception e) {
e.printStackTrace();
@@ -2677,6 +2681,16 @@ public class JgyUtils {
private static long lastTime;
/**
* 服务错峰启动间隔(毫秒),避免多个服务同时启动造成主线程串行阻塞
*/
private static final long SERVICE_START_INTERVAL = 1500L;
/**
* 服务错峰启动用的主线程 Handler
*/
private static final Handler sStartServiceHandler = new Handler(Looper.getMainLooper());
public static void startServices(Context context) {
Log.e(TAG, "startServices: " + context.getClass().getSimpleName());
if (System.currentTimeMillis() - lastTime < 10 * 60 * 1000) {
@@ -2692,8 +2706,30 @@ public class JgyUtils {
} else {
context.startService(new Intent(context, MainService.class));
}
context.startService(new Intent(context, StepService.class));
context.startService(new Intent(context, GuardService.class));
//三个服务同时启动会在开机阶段造成主线程串行阻塞(曾出现 Service 启动 200s 超时 ANR),
//这里按固定间隔错峰启动 StepService / GuardService
final Context appContext = context.getApplicationContext() != null
? context.getApplicationContext() : context;
sStartServiceHandler.postDelayed(new Runnable() {
@Override
public void run() {
try {
appContext.startService(new Intent(appContext, StepService.class));
} catch (Exception e) {
Log.e(TAG, "startServices StepService: " + e.getMessage());
}
}
}, SERVICE_START_INTERVAL);
sStartServiceHandler.postDelayed(new Runnable() {
@Override
public void run() {
try {
appContext.startService(new Intent(appContext, GuardService.class));
} catch (Exception e) {
Log.e(TAG, "startServices GuardService: " + e.getMessage());
}
}
}, SERVICE_START_INTERVAL * 2);
// context.startService(new Intent(context, LogcatService.class));
}
}
@@ -2794,7 +2830,7 @@ public class JgyUtils {
public void checkDefaultDesktop(String pkg) {
String desktopPkg = getDefaultDesktop();
// String desktopPkg = (String) SPUtils.get(mContext, "default_launcher", "");
// String desktopPkg = mMMKV.decodeString(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, "");
Log.e(TAG, "checkDefaultDesktop: pkg = " + pkg);
Log.e(TAG, "checkDefaultDesktop: desktopPkg = " + desktopPkg);
if (TextUtils.isEmpty(pkg)) {
@@ -2988,7 +3024,7 @@ public class JgyUtils {
// setDefaultLauncher(mContext, pkg, className);
Log.e(TAG, "setDefaultDesktop: setDefaultLauncher: pkg = " + pkg);
}
// String oldDesktop = (String) SPUtils.get(mContext, "default_launcher", "");
// String oldDesktop = mMMKV.decodeString(CommonConfig.DEFAULT_LAUNCHER_PACKAGE_NAME, "");
// if (Objects.equals(oldDesktop, pkg)) {
// Log.e(TAG, "setDefaultDesktop: " + "数据一致");
// return;

View File

@@ -0,0 +1,122 @@
package com.aoleyun.sn.utils;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.util.Log;
/**
* 动态注册广播兼容工具。
*
* <p>Android 13(API 33)起,动态注册的接收器若包含非系统保护广播,必须显式声明导出标志;
* Android 14+ 的系统(含各厂商定制 ROM,例如本机 Android 16)会强制校验,缺失时直接抛出:
* <pre>
* java.lang.SecurityException: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED
* should be specified when a receiver isn't being registered exclusively for system broadcasts
* </pre>
* 该异常会导致 Activity / Service 创建失败(启动闪退),并进一步造成前台服务
* {@code startForegroundService()} 超时 ANR。
*
* <p>说明:本工程通过 {@code app/libs/framework.jar} 提供系统隐藏 API(该 jar 为
* Android 10 / API 29 的 framework),其中已包含
* {@code registerReceiver(BroadcastReceiver, IntentFilter, int)} 等带 flags 的重载,
* 因此这里直接调用即可,无需反射;在低版本系统中未知标志位会被忽略,
* 行为与改造前的两参注册保持一致。
*
* <p>标志选择建议:
* <ul>
* <li>{@link #RECEIVER_NOT_EXPORTED}:只接收系统广播,或本应用
* (含共享 {@code android.uid.system} 的兄弟应用)发送的广播;</li>
* <li>{@link #RECEIVER_EXPORTED}:需要接收其它(不同 UID 的)应用发送的广播,
* 与改造前两参 {@code registerReceiver} 的默认行为一致。</li>
* </ul>
*
* @author LiGuangMin
*/
public final class ReceiverCompat {
private static final String TAG = "ReceiverCompat";
/**
* 对应 Context.RECEIVER_EXPORTED(API 33)。
*/
public static final int RECEIVER_EXPORTED = 0x00000002;
/**
* 对应 Context.RECEIVER_NOT_EXPORTED(API 33)。
*/
public static final int RECEIVER_NOT_EXPORTED = 0x00000004;
/**
* 带导出标志的注册重载从 API 33 开始对应用生效。
* 此处不使用 {@code Build.VERSION_CODES.TIRAMISU},因为 framework.jar 只到 API 29。
*/
private static final int FLAGS_API = 33;
private ReceiverCompat() {
}
/**
* 兼容注册,等价于 {@code registerReceiver(receiver, filter)},
* 但在 API 33+ 上显式带上导出标志。
*/
public static Intent registerReceiver(Context context, BroadcastReceiver receiver,
IntentFilter filter, int flags) {
return registerReceiver(context, receiver, filter, flags, null);
}
/**
* 安全注销广播接收器。
*
* <p>系统自带的 {@code unregisterReceiver()} 在"接收器未注册过"或"重复注销"时会抛出
* {@code IllegalArgumentException: Receiver not registered},直接导致进程崩溃;
* 而业务代码通常只能用字段是否为 null 来判断,字段非 null 并不代表注册成功
* (例如注册过程中抛异常、Service 被销毁等场景)。这里统一做容错处理。
*
* @param receiver 允许为 null
*/
public static void unregisterReceiver(Context context, BroadcastReceiver receiver) {
if (context == null || receiver == null) {
return;
}
try {
context.unregisterReceiver(receiver);
} catch (Exception e) {
Log.e(TAG, "unregisterReceiver failed: " + e);
}
}
/**
* 兼容注册,等价于
* {@code registerReceiver(receiver, filter, broadcastPermission, scheduler)}。
*
* @param receiver 可以为 null,此时用于查询粘性广播
* @param flags {@link #RECEIVER_EXPORTED} 或 {@link #RECEIVER_NOT_EXPORTED}
* @param broadcastPermission 发送方必须持有的权限,可为 null
*/
public static Intent registerReceiver(Context context, BroadcastReceiver receiver,
IntentFilter filter, int flags,
String broadcastPermission) {
if (context == null || filter == null) {
return null;
}
try {
if (Build.VERSION.SDK_INT >= FLAGS_API) {
return context.registerReceiver(receiver, filter, broadcastPermission, null, flags);
}
//Android 13 以下不支持导出标志,保持原有注册方式
return context.registerReceiver(receiver, filter, broadcastPermission, null);
} catch (Exception e) {
//兜底:极端情况下(例如厂商裁剪了重载)退回到传统注册,避免直接崩溃
Log.e(TAG, "registerReceiver failed, fallback to legacy: " + e);
try {
return context.registerReceiver(receiver, filter);
} catch (Exception inner) {
Log.e(TAG, "registerReceiver legacy failed: " + inner);
return null;
}
}
}
}

View File

@@ -1,194 +0,0 @@
package com.aoleyun.sn.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.util.Base64;
import android.widget.ImageView;
import com.aoleyun.sn.comm.CommonConfig;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
public class SPUtils {
/**
* 保存在手机里面的文件名
*/
public static final String FILE_NAME = "share_data";
/**
* 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
*/
public static void put(Context context, String key, Object object) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
if (object instanceof String) {
editor.putString(key, (String) object);
} else if (object instanceof Integer) {
editor.putInt(key, (Integer) object);
} else if (object instanceof Boolean) {
editor.putBoolean(key, (Boolean) object);
} else if (object instanceof Float) {
editor.putFloat(key, (Float) object);
} else if (object instanceof Long) {
editor.putLong(key, (Long) object);
} else {
editor.putString(key, object.toString());
}
SharedPreferencesCompat.apply(editor);
}
/**
* 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
*/
public static Object get(Context context, String key, Object defaultObject) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
if (defaultObject instanceof String) {
return sp.getString(key, (String) defaultObject);
} else if (defaultObject instanceof Integer) {
return sp.getInt(key, (Integer) defaultObject);
} else if (defaultObject instanceof Boolean) {
return sp.getBoolean(key, (Boolean) defaultObject);
} else if (defaultObject instanceof Float) {
return sp.getFloat(key, (Float) defaultObject);
} else if (defaultObject instanceof Long) {
return sp.getLong(key, (Long) defaultObject);
}
return null;
}
/**
* 移除某个key值已经对应的值
*/
public static void remove(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove(key);
SharedPreferencesCompat.apply(editor);
}
/**
* 清除所有数据
*/
public static void clear(Context context) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
SharedPreferencesCompat.apply(editor);
reductFirstEnter(context);
}
//还原状态firstEnter信息
private static void reductFirstEnter(Context context) {
put(context, CommonConfig.FLAG_FIRST_ENTER, CommonConfig.FLAG_FIRST_ENTER);
}
/**
* 查询某个key是否已经存在
*/
public static boolean contains(Context context, String key) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
return sp.contains(key);
}
/**
* 返回所有的键值对
*/
public static Map<String, ?> getAll(Context context) {
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
return sp.getAll();
}
/**
* 保存图片到SharedPreferences
*
* @param mContext
* @param imageView
*/
public static void putImage(Context mContext, String key, ImageView imageView) {
BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
Bitmap bitmap = drawable.getBitmap();
// 将Bitmap压缩成字节数组输出流
ByteArrayOutputStream byStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 80, byStream);
// 利用Base64将我们的字节数组输出流转换成String
byte[] byteArray = byStream.toByteArray();
String imgString = new String(Base64.encodeToString(byteArray, Base64.DEFAULT));
// 将String保存shareUtils
SPUtils.put(mContext, key, imgString);
}
/**
* 从SharedPreferences读取图片
*
* @param mContext
* @param imageView
*/
public static Bitmap getImage(Context mContext, String key, ImageView imageView) {
String imgString = (String) SPUtils.get(mContext, key, "");
if (!imgString.equals("")) {
// 利用Base64将我们string转换
byte[] byteArray = Base64.decode(imgString, Base64.DEFAULT);
ByteArrayInputStream byStream = new ByteArrayInputStream(byteArray);
// 生成bitmap
return BitmapFactory.decodeStream(byStream);
}
return null;
}
/**
* 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类
*/
private static class SharedPreferencesCompat {
private static final Method sApplyMethod = findApplyMethod();
/**
* 反射查找apply的方法
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private static Method findApplyMethod() {
try {
Class clz = SharedPreferences.Editor.class;
return clz.getMethod("apply");
} catch (NoSuchMethodException e) {
}
return null;
}
/**
* 如果找到则使用apply执行,否则使用commit
*/
public static void apply(SharedPreferences.Editor editor) {
try {
if (sApplyMethod != null) {
sApplyMethod.invoke(editor);
return;
}
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
editor.commit();
}
}
}

View File

@@ -37,6 +37,8 @@ import java.util.List;
public class SysSettingUtils {
private static final String TAG = "SysSettingUtils";
private static final MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
public SysSettingUtils() {
}
@@ -65,7 +67,7 @@ public class SysSettingUtils {
setAutoTime(context, jsonObject);
String navJsonString = jsonObject.get("setting_nav").getAsString();
JsonObject navJson = JsonParser.parseString(navJsonString).getAsJsonObject();
SPUtils.put(context, JGYActions.ACTION_STATUSBAR_STATUS, navJsonString);
mMMKV.encode(JGYActions.ACTION_STATUSBAR_STATUS, navJsonString);
setStatusBar(context, navJson);
setBrowserInput(context, jsonObject);
// TODO: 2022/4/11 不是酷比定制的会报错,无法抛出异常
@@ -113,6 +115,9 @@ public class SysSettingUtils {
setNotification(context, 0);
setSettingsMenuShow(context);
setEyeProtectionModeEntrance(context, 1);
if (JgyUtils.getInstance().checkAppPlatform() == JgyUtils.DX11Platform) {
Settings.System.putInt(context.getContentResolver(), CommonConfig.AOLE_ACTION_DEVELOPER_OPTIONS, 1);
}
}
/**
@@ -501,7 +506,7 @@ public class SysSettingUtils {
AllwinnerCubeMdmManager.getInstance().setBluetoothEnable(setting_bht == 0);
}
int bt_rename = changeNum(jsonObject.get("bluetooth_name_change").getAsInt());
int bt_rename = jsonObject.get("bluetooth_name_change").getAsInt();
Settings.System.putInt(context.getContentResolver(), CommonConfig.AOLE_ACTION_BT_RENAME_ALLOW, bt_rename);
}
@@ -1347,11 +1352,13 @@ public class SysSettingUtils {
if (power_saving_mode == 0) {
menus.add("battery");
}
// // 安全 (默认允许)
// int security = jsonObject.get("security").getAsInt();
// if (security == 0) {
// menus.add("security");
// }
// 安全 (默认允许)
int security = jsonObject.get("security").getAsInt();
if (security == 0) {
menus.add("security");
}
// // 大屏特色功能 (默认允许)
// int large_screen_features = jsonObject.get("large_screen_features").getAsInt();
// if (large_screen_features == 0) {
@@ -1383,10 +1390,11 @@ public class SysSettingUtils {
menus.add("apps");
}
// // 荣耀账号登录 (默认禁止)
// int honor_account_login = jsonObject.get("honor_account_login").getAsInt();
// if (honor_account_login == 0) {
int honor_account_login = jsonObject.get("honor_account_login").getAsInt();
if (honor_account_login == 0) {
// menus.add("honor_id");
// }
menus.add("account_id");
}
// // 智慧互联 (默认禁止)
// int smart_interconnect = jsonObject.get("smart_interconnect").getAsInt();
// if (smart_interconnect == 0) {
@@ -1412,12 +1420,21 @@ public class SysSettingUtils {
// if (app_startup_management == 0) {
// menus.add("apps_startup_management");
// }
// 应用管理 (默认禁止)
// int app_management_control = jsonObject.get("app_management_control").getAsInt();
// if (app_management_control == 0) {
int app_management_control = jsonObject.get("app_management_control").getAsInt();
if (app_management_control == 0) {
// menus.add("apps_management");
// menus.add("apps_honor_tips");
// }
menus.add("apps");
}
// 儿童 (默认禁止)
int children_mode = jsonObject.get("children_mode").getAsInt();
if (children_mode == 0) {
menus.add("kids_mode");
}
Log.e(TAG, "setSettingsMenuShow: " + menus);
Settings.System.putString(context.getContentResolver(), CommonConfig.AOLE_SETTINGS_DISALLOW, String.join(",", menus));
}

View File

@@ -11,7 +11,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.aoleyun.sn.bean.PoweroffBean;
import com.aoleyun.sn.comm.CommonConfig;
import com.aoleyun.sn.service.main.MainService;
import com.tencent.mmkv.MMKV;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
@@ -37,6 +39,8 @@ public class TimeUtils {
public static long dayTime = 60 * 60 * 24 * 1000;
public static long minuteTime = 60 * 1000;
private static final MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
public static String getNowTime() {
long nowTime = System.currentTimeMillis();
DateFormat df = ContralTime.getDf();
@@ -183,8 +187,8 @@ public class TimeUtils {
throw new RuntimeException("Time format error!");
}
try {
SPUtils.put(context, START_TIME_KEY, time[0].trim());
SPUtils.put(context, END_TIME_KEY, time[1].trim());
mMMKV.encode(START_TIME_KEY, time[0].trim());
mMMKV.encode(END_TIME_KEY, time[1].trim());
Date startDate = df.parse(time[0].trim());
Date endDate = df.parse(time[1].trim());
ContralTime contralTime = new ContralTime();
@@ -203,8 +207,8 @@ public class TimeUtils {
}
public static ContralTime getDefaltContralTime(Context context) {
String startTime = (String) SPUtils.get(context, START_TIME_KEY, "00:00");
String endTime = (String) SPUtils.get(context, END_TIME_KEY, "00:00");
String startTime = mMMKV.decodeString(START_TIME_KEY, "00:00");
String endTime = mMMKV.decodeString(END_TIME_KEY, "00:00");
if (null == startTime || null == endTime || ("00:00".equals(startTime) && "00:00".equals(endTime))) {
return null;
} else {
@@ -223,8 +227,8 @@ public class TimeUtils {
}
public static void setEmpty(Context context) {
SPUtils.put(context, START_TIME_KEY, "00:00");
SPUtils.put(context, END_TIME_KEY, "00:00");
mMMKV.encode(START_TIME_KEY, "00:00");
mMMKV.encode(END_TIME_KEY, "00:00");
Intent intent = new Intent();
intent.setAction(MainService.TimeChangedReceiver.ACTION_UPDATE);
context.sendBroadcast(intent);

View File

@@ -7,6 +7,9 @@ import android.text.TextUtils;
import android.util.Log;
import android.webkit.WebSettings;
import com.aoleyun.sn.comm.CommonConfig;
import com.tencent.mmkv.MMKV;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
@@ -37,6 +40,8 @@ public class URLUtils {
private long time1;
private long time2;
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
public URLUtils(Context context) {
this.mContext = context;
}
@@ -45,12 +50,12 @@ public class URLUtils {
time1 = System.currentTimeMillis();
//会写入两次,第一次是不带前缀的,第二次会把实际的地址写入进去
String oldwhiteList = Settings.System.getString(mContext.getContentResolver(), "DeselectBrowserArray");
String oldWhiteIP = (String) SPUtils.get(mContext, "white_ip", "");
String oldWhiteIP = mMMKV.decodeString("white_ip", "");
if (TextUtils.isEmpty(oldwhiteList) && TextUtils.isEmpty(oldWhiteIP)) {
Log.e(TAG, "getBrowserWhiteList: " + "oldwhiteList is empty");
Settings.System.putString(mContext.getContentResolver(), "DeselectBrowserArray", "Invalid");
} else {
String white_ip = (String) SPUtils.get(mContext, "white_ip", "");
String white_ip = mMMKV.decodeString("white_ip", "");
if (!TextUtils.isEmpty(white_ip)) {
whiteIPList = new HashSet<>(Arrays.asList(white_ip.split(",")));
}
@@ -221,7 +226,7 @@ public class URLUtils {
if (TextUtils.isEmpty(qch_webblack_url)) {
Settings.System.putString(mContext.getContentResolver(), "qch_webblack_url", "Invalid");
} else {
String black_ip = (String) SPUtils.get(mContext, "black_ip", "");
String black_ip = mMMKV.decodeString("black_ip", "");
if (!TextUtils.isEmpty(black_ip)) {
blackIPList = new HashSet<>(Arrays.asList(black_ip.split(",")));
}

View File

@@ -14,7 +14,6 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -59,6 +58,7 @@ import com.aoleyun.sn.comm.CommonConfig;
import com.aoleyun.sn.manager.AllwinnerCubeMdmManager;
import com.arialyy.aria.core.Aria;
import com.blankj.utilcode.util.FileUtils;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
@@ -67,6 +67,7 @@ import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.hjq.toast.Toaster;
import com.tencent.mmkv.MMKV;
import java.io.BufferedReader;
import java.io.DataOutputStream;
@@ -115,6 +116,8 @@ import vendor.mediatek.hardware.nvram.V1_0.INvram;
public class Utils {
private static final String TAG = "Utils";
private static final MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
// MD5 设备地址标识
public static String getMAC(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 如果当前设备系统大于等于6.0 使用下面的方法
@@ -597,22 +600,6 @@ public class Utils {
return intent;
}
// 设置系统亮度模式
public static void systemBrightness(Context context) {
try {
SharedPreferences mPrefs = context.getSharedPreferences("colorflykids", 0);
boolean initSetting = mPrefs.getBoolean("init_setting", true);
if (initSetting) {
mPrefs.edit().putBoolean("init_setting", false).commit();
}
Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, 0);
mPrefs.edit().putBoolean("first_init", false).commit();
} catch (Exception err) {
err.printStackTrace();
}
}
// 更新 版本比较
public static boolean isUpdate(String oldVersion, String newVersion) {
if (TextUtils.isEmpty(oldVersion) || TextUtils.isEmpty(newVersion) || oldVersion.equals(newVersion)) {
@@ -1205,47 +1192,57 @@ public class Utils {
*/
public static void ariaDownload(Context context, String url, JsonObject jsonObject) {
Log.e(TAG, "ariaDownload: " + jsonObject);
String fileName = getFileNamefromURL(url);
String urlMD5 = "";
if (jsonObject.get("MD5") == null) {
if (jsonObject.get("app_md5") != null) {
urlMD5 = jsonObject.get("app_md5").getAsString();
}
} else {
if (jsonObject.get("MD5") != null) {
if (!jsonObject.get("MD5").isJsonNull()) {
urlMD5 = jsonObject.get("MD5").getAsString();
}
}
}
final String fileName = getFileNamefromURL(url);
final String urlMD5 = resolveUrlMD5(jsonObject);
Log.e("ariaDownload", "urlMD5=" + urlMD5);
String p = JgyUtils.getInstance().getDownLoadPath();
File file = new File(JgyUtils.getInstance().getDownLoadPath() + fileName);
if (file.exists() && !file.isDirectory()) {
String filenameMD5 = getMD5fromFileName(url);
String fileMD5 = FileUtils.getFileMD5ToString(file);
Log.e("ariaDownload", "filenameMD5=" + filenameMD5);
Log.e("ariaDownload", "fileMD5=" + fileMD5);
//后端还没有完成json字段的MD5,暂时用文件名获取
if (fileMD5.equalsIgnoreCase(urlMD5)) {
ApkUtils.installApp(context, file.getAbsolutePath());
} else {
Aria.download(context)
.load(url) //读取下载地址
.setFilePath(JgyUtils.getInstance().getDownLoadPath() + fileName)
.ignoreFilePathOccupy()
.setExtendField(jsonObject.toString())
.create(); //启动下载}
final File file = new File(JgyUtils.getInstance().getDownLoadPath() + fileName);
// 校验安装包 MD5 需要完整读取文件,属于耗时 IO;安装 APK 也可能抛异常,
// 统一放到子线程执行,避免阻塞主线程(历史上这里曾导致主线程闪退/ANR)
new Thread(() -> {
try {
if (file.exists() && !file.isDirectory()) {
String filenameMD5 = getMD5fromFileName(url);
String fileMD5 = FileUtils.getFileMD5ToString(file);
Log.e("ariaDownload", "filenameMD5=" + filenameMD5);
Log.e("ariaDownload", "fileMD5=" + fileMD5);
//后端还没有完成json字段的MD5,暂时用文件名获取
if (fileMD5.equalsIgnoreCase(urlMD5)) {
ApkUtils.installApp(context, file.getAbsolutePath());
return;
}
}
} catch (Throwable e) {
Log.e("ariaDownload", "检查安装包异常: " + e.getMessage(), e);
}
} else {
startAriaDownload(context, url, fileName, jsonObject);
}, "apk-install-check").start();
}
Aria.download(context)
.load(url) //读取下载地址
.setFilePath(JgyUtils.getInstance().getDownLoadPath() + fileName)
.ignoreFilePathOccupy()
.setExtendField(jsonObject.toString())
.create(); //启动下载}
/**
* 解析下载 JSON 中的 MD5 字段,兼容 MD5 / app_md5 两种命名。
*/
private static String resolveUrlMD5(JsonObject jsonObject) {
String urlMD5 = "";
JsonElement md5Element = jsonObject.get("MD5");
if (md5Element == null || md5Element.isJsonNull()) {
md5Element = jsonObject.get("app_md5");
}
if (md5Element != null && !md5Element.isJsonNull()) {
urlMD5 = md5Element.getAsString();
}
return urlMD5;
}
/**
* 提交 Aria 下载任务。
*/
private static void startAriaDownload(Context context, String url, String fileName, JsonObject jsonObject) {
Aria.download(context)
.load(url) //读取下载地址
.setFilePath(JgyUtils.getInstance().getDownLoadPath() + fileName)
.ignoreFilePathOccupy()
.setExtendField(jsonObject.toString())
.create(); //启动下载}
}
private void showToast() {
@@ -1485,7 +1482,7 @@ public class Utils {
jsonObject.addProperty("rom", rom);
jsonObject.addProperty("screen_rate", screen_rate);
jsonObject.addProperty("working_time", SystemClock.elapsedRealtime() / 1000);
jsonObject.addProperty("restart_count", (int) SPUtils.get(context, "restart_count", 0));
jsonObject.addProperty("restart_count", mMMKV.decodeInt("restart_count", 0));
jsonObject.addProperty("storage_usable", getDataTotalSizeGb(context));
jsonObject.addProperty("storage_total", getDataTotalSizeGb(context));
String jsonString = jsonObject.toString();
@@ -1517,12 +1514,12 @@ public class Utils {
}
public static String getAddressJsonString(Context context) {
String address = String.valueOf(SPUtils.get(context, "MapAddress", "-"));
String address = String.valueOf(mMMKV.decodeString("MapAddress", "-"));
if ("-".equals(address)) {
address = "定位失败";
}
String longitude = String.valueOf(SPUtils.get(context, "longitude", "0"));
String latitude = String.valueOf(SPUtils.get(context, "latitude", "0"));
String longitude = String.valueOf(mMMKV.decodeString("longitude", "0"));
String latitude = String.valueOf(mMMKV.decodeString("latitude", "0"));
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("address", address);
jsonObject.addProperty("longitude", longitude);
@@ -1616,7 +1613,8 @@ public class Utils {
if (Build.VERSION.SDK_INT >= 21) {
return ((BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE)).getIntProperty(4);
} else {
Intent intent = (new ContextWrapper(mContext)).registerReceiver(null, new IntentFilter("android.intent.action.BATTERY_CHANGED"));
Intent intent = ReceiverCompat.registerReceiver(new ContextWrapper(mContext), null,
new IntentFilter("android.intent.action.BATTERY_CHANGED"), ReceiverCompat.RECEIVER_NOT_EXPORTED);
return intent.getIntExtra("level", -1) * 100 / intent.getIntExtra("scale", -1);
}
}
@@ -1624,7 +1622,7 @@ public class Utils {
public static int getBatteryPercentage(Context context) {
IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, iFilter);
Intent batteryStatus = ReceiverCompat.registerReceiver(context, null, iFilter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
int level = batteryStatus != null ? batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) : -1;
int scale = batteryStatus != null ? batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1) : -1;
@@ -1636,7 +1634,7 @@ public class Utils {
public static int getIsCharging(Context context) {
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
Intent batteryStatus = ReceiverCompat.registerReceiver(context, null, ifilter, ReceiverCompat.RECEIVER_NOT_EXPORTED);
// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||

View File

@@ -323,40 +323,81 @@ public class XAPKUtils {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static void installAppatPie(Context context, List<String> apkFilePath) {
Log.e(TAG, "installApps: 正在安装应用 " + apkFilePath);
try {
Log.e(TAG, "installApps: 正在安装应用 " + apkFilePath);
// File file = new File(apkFilePath);
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller
.SessionParams.MODE_FULL_INSTALL);
sessionParams.setSize(XAPKSize);
int sessionId = createSession(packageInstaller, sessionParams);
if (sessionId != -1) {
for (String apkPath : apkFilePath) {
copyApkFile(packageInstaller, sessionId, apkPath);
}
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller
.SessionParams.MODE_FULL_INSTALL);
sessionParams.setSize(XAPKSize);
int sessionId = createSession(packageInstaller, sessionParams);
if (sessionId != -1) {
for (String apkPath : apkFilePath) {
copyApkFile(packageInstaller, sessionId, apkPath);
}
// boolean copySuccess = ;
// if (copySuccess) {
Toaster.show("正在安装应用");
install(packageInstaller, sessionId, context);
Toaster.show("正在安装应用");
install(packageInstaller, sessionId, context);
// }
//
}
} catch (Throwable e) {
// 安装失败只记录日志,避免异常冒泡导致进程闪退
Log.e(TAG, "installAppatPie: 安装异常 " + e.getMessage(), e);
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private static void install(PackageInstaller packageInstaller, int sessionId, Context context) {
PackageInstaller.Session session = null;
try {
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
Intent intent = new Intent(context, InstallResultReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
1, intent,
PendingIntent.FLAG_UPDATE_CURRENT
);
session.commit(pendingIntent.getIntentSender());
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "install: " + e.getMessage());
session = packageInstaller.openSession(sessionId);
session.commit(createResultPendingIntent(context).getIntentSender());
} catch (Throwable e) {
Log.e(TAG, "install: 安装失败 " + e.getMessage(), e);
abandonSession(packageInstaller, sessionId);
} finally {
if (session != null) {
try {
session.close();
} catch (Throwable ignore) {
}
}
}
}
/**
* Android 14(targetSdk 34+)起,PackageInstaller 的 commit 结果接收器必须是可变
* (Mutable)PendingIntent,系统需要向其中回填安装结果,否则 commit() 会抛出
* IllegalArgumentException 并导致进程闪退。
*/
private static PendingIntent createResultPendingIntent(Context context) {
Intent intent = new Intent(context, InstallResultReceiver.class);
intent.setPackage(context.getPackageName());
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
if (Build.VERSION.SDK_INT >= ANDROID_12) {
flags |= FLAG_MUTABLE;
}
return PendingIntent.getBroadcast(context, 1, intent, flags);
}
/**
* Android 12(API 31)。编译使用的 framework.jar 为设备提取版,不含 API 31 新增符号,
* 因此这里使用字面量常量代替 {@code Build.VERSION_CODES.S} 与 {@code PendingIntent.FLAG_MUTABLE}。
*/
private static final int ANDROID_12 = 31;
/**
* PendingIntent.FLAG_MUTABLE(API 31 新增,值为 1 << 25)。
*/
private static final int FLAG_MUTABLE = 1 << 25;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private static void abandonSession(PackageInstaller packageInstaller, int sessionId) {
try {
packageInstaller.abandonSession(sessionId);
} catch (Throwable e) {
Log.e(TAG, "abandonSession: " + e.getMessage());
}
}