Android Q已經正式發佈了,其中有一條隱私性調整,對於國內應用的影響很大。 那就是標題所說的 「禁止應用後臺私自啓動Activity」,確實,對於用戶來講,無論哪一個方面都是好事。java
就是這個功能,致使了應用保活更加困難,做爲用戶,我是支持禁止的。做爲開發者,我也是支持禁止的。可是本着探究的目的,仍是決定研究一下。android
另外,因爲我是第一次看這個源碼,自己不太理解內部的結構,因此更多的是經過關鍵字搜索看一些有特徵的方法或者代碼,而對於方法或者屬性沒有比較深的理解,浮於表面,因此內容包含了大量的源碼。閱讀體驗可能不佳。請見諒。bash
通過源碼分析,基本沒法直接繞過檢查機制。可是能夠考慮經過系統服務假裝或者間接調起。 具體間接繞過方式尚未研究結果。app
在拒絕Activity後臺啓動時,會產生相應的Log,如:ide
09-10 02:37:42.615 1967 2087 I ActivityTaskManager: START u0 {flg=0x10000004 cmp=com.lollipop.startactivitywhenbackground/.MainActivity (has extras)} from uid 10131
09-10 02:37:42.618 1967 2087 W ActivityTaskManager: Background activity start [callingPackage: com.lollipop.startactivitywhenbackground; callingUid: 10131; isCallingUidForeground: false; isCallingUidPersistentSystemProcess: false; realCallingUid: 1000; isRealCallingUidForeground: false; isRealCallingUidPersistentSystemProcess: true; originatingPendingIntent: PendingIntentRecord{583ac97 com.lollipop.startactivitywhenbackground startActivity}; isBgStartWhitelisted: false; intent: Intent { flg=0x10000004 cmp=com.lollipop.startactivitywhenbackground/.MainActivity (has extras) }; callerApp: null]
複製代碼
從log上看,Log是從ActivityTaskManager
發出的,前往 AndroidXref 的Android Pie
中查看(尚未Android Q
的源碼),發現沒有搜索結果,應該是Android Q
新增的。 只能前往Google Git尋找,因爲沒有搜索功能(我不太會用)只能 傻敷敷老實的挨個找,最終尋找到ActivityTaskManager.java,可是很惋惜,只是一個包裝類,內部實現是:源碼分析
/** @hide */
public static IActivityTaskManager getService() {
return IActivityTaskManagerSingleton.get();
}
@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
new Singleton<IActivityTaskManager>() {
@Override
protected IActivityTaskManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityTaskManager.Stub.asInterface(b);
}
};
複製代碼
而後理所固然的就是放棄了,而後經過 Pie 的代碼,來搜索,看看這個服務在哪裏初始化的。 最終,找到了 SystemServer.java , 接着又在其中找到了具體實現類ActivityTaskManagerService.java
的包路徑。ui
import com.android.server.wm.ActivityTaskManagerService;
複製代碼
接着,循着這個包路徑也就找到了文件的真實路徑了:ActivityTaskManagerService.java 。this
從中,經過background
關鍵字搜索,找到了以下方法:google
@Override
public final int startActivities(IApplicationThread caller, String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId) {
final String reason = "startActivities";
enforceNotIsolatedCaller(reason);
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
// TODO: Switch to user app stacks here.
return getActivityStartController().startActivities(caller, -1, 0, -1, callingPackage,
intents, resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId,
reason, null /* originatingPendingIntent */,
false /* allowBackgroundActivityStart */);
}
@Override
public int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
true /*validateIncomingUser*/);
}
int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
enforceNotIsolatedCaller("startActivityAsUser");
userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
// TODO: Switch to user app stacks here.
return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setMayWait(userId)
.execute();
}
複製代碼
上面的代碼中,批量啓動方法是直接顯式設置allowBackgroundActivityStart
爲false
。而 startActivityAsUser
倒是沒有設置,保持了缺省值。 getActivityStartController()
的返回對象爲:ActivityStartController.java。 它的 obtainStarter()
方法細節爲:spa
/** * @return A starter to configure and execute starting an activity. It is valid until after * {@link ActivityStarter#execute} is invoked. At that point, the starter should be * considered invalid and no longer modified or used. */
ActivityStarter obtainStarter(Intent intent, String reason) {
return mFactory.obtain().setIntent(intent).setReason(reason);
}
複製代碼
返回了一個 ActivityStarter.java 對象,而前面設置的參數,都保存在內部類 Request
中,其中的缺省值設置是:
/** * Ensure constructed request matches reset instance. */
Request() {
reset();
}
/** * Sets values back to the initial state, clearing any held references. */
void reset() {
...
allowBackgroundActivityStart = false;
}
複製代碼
默認狀況下,是拒絕後臺啓動的。也就是說,用戶啓動的話,禁止在後臺直接啓動的。 具體的判斷代碼是在 shouldAbortBackgroundActivityStart
方法中,由於這個方法算是判斷的核心方法了,所以貼了完整代碼。
boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, final String callingPackage, int realCallingUid, int realCallingPid, WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart, Intent intent) {
// don't abort for the most important UIDs
final int callingAppId = UserHandle.getAppId(callingUid);
if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
|| callingAppId == Process.NFC_UID) {
return false;
}
// don't abort if the callingUid has a visible window or is a persistent system process
final int callingUidProcState = mService.getUidState(callingUid);
final boolean callingUidHasAnyVisibleWindow =
mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow
|| callingUidProcState == ActivityManager.PROCESS_STATE_TOP
|| callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
final boolean isCallingUidPersistentSystemProcess =
callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) {
return false;
}
// take realCallingUid into consideration
final int realCallingUidProcState = (callingUid == realCallingUid)
? callingUidProcState
: mService.getUidState(realCallingUid);
final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
? callingUidHasAnyVisibleWindow
: mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(realCallingUid);
final boolean isRealCallingUidForeground = (callingUid == realCallingUid)
? isCallingUidForeground
: realCallingUidHasAnyVisibleWindow
|| realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP;
final int realCallingAppId = UserHandle.getAppId(realCallingUid);
final boolean isRealCallingUidPersistentSystemProcess = (callingUid == realCallingUid)
? isCallingUidPersistentSystemProcess
: (realCallingAppId == Process.SYSTEM_UID)
|| realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
if (realCallingUid != callingUid) {
// don't abort if the realCallingUid has a visible window
if (realCallingUidHasAnyVisibleWindow) {
return false;
}
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't whitelisted to start an activity
if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
return false;
}
// don't abort if the realCallingUid is an associated companion app
if (mService.isAssociatedCompanionApp(UserHandle.getUserId(realCallingUid),
realCallingUid)) {
return false;
}
}
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PERMISSION_GRANTED) {
return false;
}
// don't abort if the caller has the same uid as the recents component
if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
return false;
}
// don't abort if the callingUid is the device owner
if (mService.isDeviceOwner(callingUid)) {
return false;
}
// don't abort if the callingUid has companion device
final int callingUserId = UserHandle.getUserId(callingUid);
if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) {
return false;
}
// If we don't have callerApp at this point, no caller was provided to startActivity().
// That's the case for PendingIntent-based starts, since the creator's process might not be
// up and alive. If that's the case, we retrieve the WindowProcessController for the send()
// caller, so that we can make the decision based on its foreground/whitelisted state.
int callerAppUid = callingUid;
if (callerApp == null) {
callerApp = mService.getProcessController(realCallingPid, realCallingUid);
callerAppUid = realCallingUid;
}
// don't abort if the callerApp or other processes of that uid are whitelisted in any way
if (callerApp != null) {
// first check the original calling process
if (callerApp.areBackgroundActivityStartsAllowed()) {
return false;
}
// only if that one wasn't whitelisted, check the other ones
final ArraySet<WindowProcessController> uidProcesses =
mService.mProcessMap.getProcesses(callerAppUid);
if (uidProcesses != null) {
for (int i = uidProcesses.size() - 1; i >= 0; i--) {
final WindowProcessController proc = uidProcesses.valueAt(i);
if (proc != callerApp && proc.areBackgroundActivityStartsAllowed()) {
return false;
}
}
}
}
// don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
Slog.w(TAG, "Background activity start for " + callingPackage
+ " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
return false;
}
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
+ "; isCallingUidForeground: " + isCallingUidForeground
+ "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess
+ "; realCallingUid: " + realCallingUid
+ "; isRealCallingUidForeground: " + isRealCallingUidForeground
+ "; isRealCallingUidPersistentSystemProcess: "
+ isRealCallingUidPersistentSystemProcess
+ "; originatingPendingIntent: " + originatingPendingIntent
+ "; isBgStartWhitelisted: " + allowBackgroundActivityStart
+ "; intent: " + intent
+ "; callerApp: " + callerApp
+ "]");
// log aborted activity start to TRON
if (mService.isActivityStartsLoggingEnabled()) {
mSupervisor.getActivityMetricsLogger().logAbortedBgActivityStart(intent, callerApp,
callingUid, callingPackage, callingUidProcState, callingUidHasAnyVisibleWindow,
realCallingUid, realCallingUidProcState, realCallingUidHasAnyVisibleWindow,
(originatingPendingIntent != null));
}
return true;
}
複製代碼
上面方法中的判斷條件,就是容許後臺啓動 Activity
的所有條件了。 上面條件中,有3個是比較明顯的。
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't whitelisted to start an activity
if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
return false;
}
// don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
Slog.w(TAG, "Background activity start for " + callingPackage
+ " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
return false;
}
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PERMISSION_GRANTED) {
return false;
}
複製代碼
一個是前面顯式設置的白名單屬性allowBackgroundActivityStart
, 另外一個是應用的SYSTEM_ALERT_WINDOW
權限,還有一個就是後臺啓動Activity
的權限。
到了這裏,基本上研究就算結束了,剩下的就是怎麼去繞過,可是目前我沒有想到辦法繞過。 另外,在 ActivityManagerService.java 中也發現了一個權限:
@GuardedBy("this")
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, int realCallingPid, int userId, boolean allowBackgroundActivityStarts) {
intent = new Intent(intent);
...
if (bOptions != null) {
if (brOptions.allowsBackgroundActivityStarts()) {
// See if the caller is allowed to do this. Note we are checking against
// the actual real caller (not whoever provided the operation as say a
// PendingIntent), because that who is actually supplied the arguments.
if (checkComponentPermission(
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
realCallingPid, realCallingUid, -1, true)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: " + intent.getAction()
+ " broadcast from " + callerPackage + " (pid=" + callingPid
+ ", uid=" + callingUid + ")"
+ " requires "
+ android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
Slog.w(TAG, msg);
throw new SecurityException(msg);
} else {
allowBackgroundActivityStarts = true;
}
}
}
...
return ActivityManager.BROADCAST_SUCCESS;
}
複製代碼
雖然看到了這個權限的存在,可是目前尚未肯定這個權限的具體影響。
以上就是本次初步研究的結果了,雖然看到了源碼,可是卻處於無從下手的狀態。
若是看官有什麼看法或者方案,歡迎評價。