1. 目的
基于《软件绿色联盟应用体验标准》中 NetLocation 资源的定义,进 NetLocation 频繁次数的测试apk。旨在触发手机中异常功耗管控机制。
2. 测试步骤
H手机和T手机、其他手机进行安装该apk.
所有手机都需要设置应用为白名单。
2.1 手机白名单设置方法:
手机管家->应用启动设置:允许自启动、允许关联启动、允许后台启动
2.2 测试环境
- 必须开启GPS和WiFi连接,再打开app进行测试
- home 回到桌面,灭屏至少30分钟以上,再亮屏查看消息通知栏
2.2 dump 相关命令
2.2.1 adb shell dumpsys appops --op X
用途X:获取手机应用定位信息
APP_OP_COARSE_LOCATION = 0
APP_OP_FINE_LOCATION = 1
APP_OP_MONITOR_LOCATION = 41
APP_OP_MONITOR_HIGH_POWER_LOCATION = 42
APP_OP_MOCK_LOCATION = 58
APP_OP_ACCESS_MEDIA_LOCATION = 90
APP_OP_FINE_LOCATION_SOURCE = 108
APP_OP_COARSE_LOCATION_SOURCE = 109
例如:adb shell dumpsys appops --op 41
$ adb shell dumpsys appops --op 41
Current AppOps Service state:
Settings:
top_state_settle_time=+5s0ms
fg_service_state_settle_time=+5s0ms
bg_state_settle_time=+1s0ms
Uid 1000:
state=pers
capability=LCMN
appWidgetVisible=false
Package android:
MONITOR_LOCATION (allow / switch COARSE_LOCATION=allow):
null=[
Access: [pers-s] 2022-04-22 17:43:07.078 (-5d4h11m26s922ms) duration=+35s984ms
]
SensorNotificationService=[
Access: [pers-s] 2022-04-20 22:12:25.441 (-6d23h42m8s559ms) duration=+2d0h15m48s945ms
]
GnssService=[
Access: [pers-s] 2022-04-22 17:43:28.020 (-5d4h11m5s980ms) duration=+9s997ms
]
2.2.2 adb shell dumpsys location
用途X:获取手机应用定位信息
2.2 运行本apk
2.3 日志查看
adb shell dumpsys appops --op 41
Uid u0a398:
state=top
startNesting=2
Package com.sufadi.blocaknetlocation:
MONITOR_LOCATION (allow / switch COARSE_LOCATION=allow):
Access: [top-s] 2022-06-06 13:57:20.258 (-1m31s751ms)
Running start at: +1m31s750ms
startNesting=1
3. apk 源码
本apk作用:仅使用网络定位,但是后台一直无限制进行网络位置监听。
3.1 UI
3.2 核心逻辑
3.2.1 MainActivity
package com.sufadi.blocaknetlocation
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
companion object {
val TAG = "blocaknetlocation_MainActivity"
val REQUES_CODE_OK = 200
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
applyPermission()
}
fun applyPermission() {
if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED){//未开启定位权限
Log.d(TAG, "applyPermission")
ActivityCompat.requestPermissions(this, arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION), REQUES_CODE_OK)
} else {
Log.d(TAG, "startService BlockNetLocationService")
startService(Intent(this, BlockNetLocationService::class.java))
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray) {
when (requestCode) {
REQUES_CODE_OK-> if (grantResults.size == 2 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
for (grant in grantResults) {
Log.d(TAG, "grant: $grant")
}
ActivityCompat.requestPermissions(this, arrayOf(
Manifest.permission.ACCESS_BACKGROUND_LOCATION), REQUES_CODE_OK)
/*Toast.makeText(this@MainActivity, "未开启定位权限,请手动到设置去开启权限", Toast.LENGTH_LONG).show()
finish()*/
} else {
Log.d(TAG, "startService BlockNetLocationService 1")
startService(Intent(this, BlockNetLocationService::class.java))
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
3.2.2 BlockNetLocationService
主要进行网络定位的监听
package com.sufadi.blocaknetlocation
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import android.os.IBinder
import android.util.Log
class BlockNetLocationService: Service() {
companion object {
val TAG = "BlockNetLocationService"
val FORGROUND_ID = 0x11
}
lateinit var locationManager: LocationManager
lateinit var netWorkListener: NetworkListener
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate")
startMyForeground()
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
netWorkListener = NetworkListener()
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000L, 0f, netWorkListener)
}
override fun onDestroy() {
super.onDestroy()
locationManager.removeUpdates(netWorkListener)
stopForeground(true)
}
inner class NetworkListener : LocationListener {
// 位置的改变
override fun onLocationChanged(location: Location) {
val latitude = location.latitude// 维度
val longitude = location.longitude// 经度
//显示当前坐标
Log.d(TAG, "网络定位 location:($latitude,$longitude)")
}
// gps卫星有一个没有找到
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {
Log.d(TAG, "网络定位 onStatusChanged:provider$provider,status: $status, extras:$extras")
}
// 某个设置被打开
override fun onProviderEnabled(provider: String) {
Log.d(TAG, "网络定位 onProviderEnabled$provider")
}
// 某个设置被关闭
override fun onProviderDisabled(provider: String) {
Log.d(TAG, "网络定位 onProviderDisabled$provider")
}
}
private fun startMyForeground() {
Log.d(TAG, "startMyForeground show notification")
Log.d(TAG, "PhoneDataService startMyForeground sdk :" + android.os.Build.VERSION.SDK_INT)
val nb = Notification.Builder(this)
if (android.os.Build.VERSION.SDK_INT >= 26) {
val CHANNEL_ONE_ID = "channel_id_foreground"
val CHANNEL_ONE_NAME = "Channel One"
var notificationChannel: NotificationChannel? = null
notificationChannel = NotificationChannel(
CHANNEL_ONE_ID,
CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_LOW
)
nb.setChannelId(CHANNEL_ONE_ID)
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(notificationChannel)
}
nb.setSmallIcon(R.mipmap.ic_launcher)
nb.setContentTitle(getString(R.string.notification_title))
nb.setContentText(getString(R.string.notification_Content))
try {
startForeground(FORGROUND_ID, nb.build())
} catch (e: Exception) {
e.printStackTrace()
}
}
}
3.3 不触发消息通知的分析
2022-06-06 17:31:41.988 2490-3391/? I/APwActAnalysis: can not process the net location app: com.sufadi.blocaknetlocation
655 private void analyseNetLocationAct(AppPowerRecord arg18, long arg19) {
656 AppPowerActAnalysis v0 = this;
657 long v10 = arg18.getNetLocationTime();
658 int v12 = 12;
659 int v13 = arg18.getPowerLevel(v12);
660 String v14 = arg18.mAppName;
661 if(v13 == 2 || v13 == 3) {
662 AppActAnalyzer v15 = v0.getAnalyzer(v12);
663 if(v15 != null) {
664 v15.initActAnalyzer(v14, v10, arg19, 1, v13);
665 int v8 = v15.startActAnalyzer();
666 if(v8 == 1) {
667 Log.i("APwActAnalysis", "a abnormal net location app, K_L : " + v14);
668 v0.mAppPowerSave.dispatchAbnormalApp(v14, v12, v10, 1, arg19);
669 }
670 else if(v8 == -1) {
671 Log.i("APwActAnalysis", "net location high power app: " + v14 + " level: " + v13 + " duration:" + v10);
672 v0.mAppPowerSave.handleHighPowerApp(v14, v12, v10, arg19);
673 }
674 else {
675 Log.i("APwActAnalysis", "can not process the net location app: " + v14);
676 }
677 }
678 else {
679 v0.mAppPowerSave.handleHighPowerApp(v14, v12, v10, arg19);
680 }
681 }
682 }
27 public int startActAnalyzer() {
// isCtrlScope 为false,则 getOptimizeType = 0, 而我们demo app 的设置为 1,isCtrlScope为true
// isPrivilegeUserApp demo app 也非写死的应用
// isImportantDefaultApp demo app 也非重要进程
// isClockApp demo app 也不是闹钟应用
28 if (!isCtrlScope() || isPrivilegeUserApp() || isImportantDefaultApp() || isClockApp()) {
29 return 0;
30 }
31 int optimizeType = this.mIAppManager.getOptimizeType(this.mPkg);
32 if (optimizeType == 2) { // 智能自动管理
33 int i = this.mPowerLevel;
34 if ((i == 2 || i == 3) && NET_LOCATION_APPS_BLACK_LIST.contains(this.mPkg)) {
35 return 1;
36 }
37 return -1;
38 } else if (optimizeType != 3) { // 不允许自启动+不允许关联启动+不允许后台启动
39 return -1;
40 } else {
41 int i2 = this.mPowerLevel;
42 if (i2 == 2 || i2 == 3) {
43 return 1;
44 }
45 return -1;
46 }
47 }
48 }
=================================================================================
1152 public boolean isCtrlScope(String str) {
1153 return getOptimizeType(str) != 0;
1154 }
1115 public int getOptimizeType(String str) {
1116 if (this.mSmartOptimizeApps.contains(str)) {
1117 return 2;
1118 }
1119 if (this.mAlwaysOptimizeApps.contains(str)) {
1120 return 3;
1121 }
1122 if (this.mNeverOptimizeApps.contains(str)) {
1123 return 1;
1124 }
1125 if ((!mHasThreeOptimize && DEBUG_USB) || mFeedbacKillSystemApps.contains(str) || this.mOutScopeCtrlApps.contains(str)) {
1126 return 2;
1127 }
1128 if (!this.mAppManager.asSmartOptimizeApp(str) || this.mSmartOptimizeHideApps.contains(str)) {
1129 return 0;
1130 }
1131 this.mOutScopeCtrlApps.add(str);
1132 Log.i("ApplistMgr", str + " as smart opt...");
1133 return 2;
1134 }
user@chuanghangren-Lenovo-Product:~$ adb shell dumpsys powergenius -a|grep "com.sufadi.blocaknetlocation"
mNeverOptimizeApps: [com.huawei.localBackup, com.huawei.appmarket, com.tencent.mm, com.sufadi.blockgps, com.sufadi.blocaknetlocation, com.unionpay, com.sufadi.blockalarm, com.huawei.hwid]
862 private void loadSmartOptHideAppsNew() {
863 List<String> appOptimizeType = AppAwareAdapter.getAppOptimizeType(new int[]{1, -1, -1, -1}, (int[]) null, new int[]{0, -1, -1, -1});
864 if (appOptimizeType != null) {
865 this.mSmartOptimizeHideApps.addAll(appOptimizeType);
866 Log.i("ApplistMgr", "mSmartOptimizeHideApps: " + this.mSmartOptimizeHideApps);
867 }
868 }
863 public boolean asSmartOptimizeApp(String str) {
864 String str2;
865 List users = this.mUserManager.getUsers();
866 if (users == null) {
867 Log.w(TAG, "users is null.");
868 return false;
869 }
870 ApplicationInfo applicationInfo = null;
871 Iterator it = users.iterator();
872 while (true) {
873 if (!it.hasNext()) {
874 break;
875 }
876 try {
877 applicationInfo = this.mPM.getApplicationInfoAsUser(str, 0, ((UserInfo) it.next()).id);
878 break;
879 } catch (PackageManager.NameNotFoundException unused) {
880 } catch (RuntimeException e) {
881 Log.e(TAG, "RuntimeException: ", e);
882 } catch (Exception e2) {
883 Log.e(TAG, "Exception: ", e2);
884 }
885 }
886 if (!isSystemApp(applicationInfo)) {
887 return true;
888 }
// 可删除的第三方应用目录
889 if (applicationInfo == null || (str2 = applicationInfo.sourceDir) == null || !str2.contains("/system/delapp")) {
890 return false;
891 }
892 return true;
893 }
=================================================================================
154 private static final ArrayList<String> mPrivilegeUserApps = new ArrayList<String>() {
155 {
156 add("com.whatsapp");
157 add("com.facebook.katana");
158 add("com.facebook.orca");
159 add("com.tencent.mm");
160 add("jp.netstar.familysmile");
161 add("com.nttdocomo.android.gesturecontrol");
162 add("jp.softbank.mb.parentalcontrols");
163 }
164 };
=================================================================================
133 public boolean isImportantDefaultApp() {
134 String stringForUser;
135 String defaultLauncher = this.mIAppType.getDefaultLauncher();
136 if (defaultLauncher != null && defaultLauncher.equals(this.mPkg)) {
137 return true;
138 }
139 String usingLauncher = this.mIAppType.getUsingLauncher();
140 if (usingLauncher != null && usingLauncher.equals(this.mPkg)) {
141 return true;
142 }
143 String curLiveWallpaper = this.mIAppType.getCurLiveWallpaper();
144 if (curLiveWallpaper != null && curLiveWallpaper.equals(this.mPkg)) {
145 return true;
146 }
147 String defaultInputMethod = this.mIAppType.getDefaultInputMethod();
148 if (defaultInputMethod != null && defaultInputMethod.equals(this.mPkg)) {
149 return true;
150 }
151 String defaultSmsApplication = this.mIAppType.getDefaultSmsApplication();
152 if (defaultSmsApplication != null && defaultSmsApplication.equals(this.mPkg)) {
153 return true;
154 }
155 int curUserId = this.mIAppManager.getCurUserId();
156 if (curUserId == 0 || (stringForUser = Settings.Secure.getStringForUser(this.mContext.getContentResolver(), "sms_default_application", curUserId)) == null || !stringForUser.equals(this.mPkg)) {
157 return false;
158 }
159 return true;
160 }
426 public static String getDefaultSmsApplication(Context context) {
427 try {
428 ComponentName component = (ComponentName) ReflectUtils.invokeMethod("getDefaultSmsApplication", "com.android.internal.telephony.SmsApplication", new Object[]{context, Boolean.valueOf(false)});
429 if (component == null) {
430 return null;
431 }
432 String defaultSmsPackage = component.getPackageName();
433 Log.i("CommonAdapter", "defaultSmsApplication: " + defaultSmsPackage);
434 return defaultSmsPackage;
435 } catch (Exception e) {
436 Log.w("CommonAdapter", "no method getDefaultSmsApplication");
437 return null;
438 }
439 }
adb shell settings get secure sms_default_application
com.android.mms
adb shell pm list users Users
Users:
UserInfo{0:+86 132 6685 2358:13} running
2022-06-08 11:36:54.276 2494-3513/? I/SmsApplication: updatedNeeded = false for userId = 0
2022-06-08 11:36:54.283 2494-3513/? I/CommonAdapter: defaultSmsApplication: com.android.mms
2022-06-08 11:36:54.284 2494-3513/? I/APwActAnalysis: can not process the net location app: com.game.map.email.alarm.network
=================================================================================
162 /* access modifiers changed from: protected */
163 public boolean isClockApp() {
164 if ("com.android.deskclock".equals(this.mPkg) || "com.huawei.deskclock".equals(this.mPkg) || "com.huawei.calendar".equals(this.mPkg) || "com.android.calendar".equals(this.mPkg)) {
165 return true;
166 }
167 ArrayList<String> appsByType = this.mIAppType.getAppsByType(10);
168 if (appsByType == null || appsByType.size() <= 0 || !appsByType.contains(this.mPkg)) {
169 return false;
170 }
171 return true;
172 }
2022-06-08 11:42:54.943 2494-3513/? I/APwActAnalysis: can not process the net location app: com.game.map.email.alarm.network
APwActAnalysis|com.sufadi.blocaknetlocation|AppPowerMonitor