Skip to content
This repository has been archived by the owner on Oct 29, 2024. It is now read-only.

Commit

Permalink
Support for getting the calling app's permission state for activities
Browse files Browse the repository at this point in the history
This allows for apps that can already access the calling app identity that launchers their activity, along with other select system apps such as browser that are granted with such access, to check whenever a permission is granted, similar to the permission entry in activity section of AndroidManifest.xml.
  • Loading branch information
quh4gko8 committed May 9, 2024
1 parent f03d5b6 commit 9351c45
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 0 deletions.
9 changes: 9 additions & 0 deletions core/java/android/app/Activity.java
Original file line number Diff line number Diff line change
Expand Up @@ -7086,6 +7086,15 @@ public String getLaunchedFromPackage() {
return ActivityClient.getInstance().getLaunchedFromPackage(getActivityToken());
}

/**
* @hide
*/
@PackageManager.PermissionResult
public int checkLaunchedFromPermission(@NonNull String permission) {
return ActivityClient.getInstance().checkLaunchedFromPermission(getActivityToken(),
permission, getDeviceId());
}

/**
* Control whether this activity's main window is visible. This is intended
* only for the special case of an activity that is not going to show a
Expand Down
8 changes: 8 additions & 0 deletions core/java/android/app/ActivityClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -694,4 +694,12 @@ protected IActivityClientController create() {
}
}
}

public int checkLaunchedFromPermission(IBinder token, String permission, int deviceId) {
try {
return getActivityClientController().checkLaunchedFromPermission(token, permission, deviceId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
6 changes: 6 additions & 0 deletions core/java/android/app/IActivityClientController.aidl
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,10 @@ interface IActivityClientController {
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.INTERNAL_SYSTEM_WINDOW)")
oneway void setActivityRecordInputSinkEnabled(in IBinder activityToken, boolean enabled);

/**
* @return {@link PackageManager#PERMISSION_GRANTED} if the app launching the activity has the permission.
* This method is only accessible to select system apps such as browser app.
*/
int checkLaunchedFromPermission(in IBinder token, String permission, int deviceId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -1705,4 +1705,16 @@ public void setActivityRecordInputSinkEnabled(IBinder activityToken, boolean ena
}
}
}

@Override
public int checkLaunchedFromPermission(IBinder token, String permission, int deviceId) {
if (!ActivityClientControllerHooks.canAccessLaunchedFromPackagePermission(permission)) {
throw new SecurityException();
}
final ActivityRecord r;
synchronized (mGlobalLock) {
r = ActivityRecord.forTokenLocked(token);
}
return ActivityClientControllerHooks.checkLaunchedFromPermission(r, permission, deviceId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.android.server.wm;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.content.Context;
import android.content.pm.PackageManager;
import android.ext.BrowserUtils;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Slog;

import com.android.internal.util.ArrayUtils;

import com.android.server.LocalServices;
import com.android.server.pm.permission.PermissionManagerServiceInternal;

class ActivityClientControllerHooks {

private static final String TAG = "ActivityClientControllerHooks";
private static final boolean DEBUG_LOGS = false;

static boolean canAccessLaunchedFromPackagePermission(@NonNull String permission) {
if (!isPermissionEligibleForChecking(permission)) {
Slog.w(TAG, "Permission supplied isn't eligible for permission checks from calling package");
return false;
}

final String[] callerPkgs;
try {
callerPkgs = AppGlobals.getPackageManager().getPackagesForUid(Binder.getCallingUid());
} catch (RemoteException e) {
Slog.w(TAG, "No packages found for calling app", e);
return false;
}

for (String callerPkgName: callerPkgs) {
if (isCallerEligibleForChecking(callerPkgName)) {
return true;
}
}

return false;
}

private static boolean isCallerEligibleForChecking(@NonNull String callerPkgName) {
if (canAccessForDebuggingPurposes(callerPkgName)) {
if (DEBUG_LOGS) {
Slog.d(TAG, "Current package " + callerPkgName + " has been allowed "
+ "to fetch permission state of calling app that opened its activities.");
}
return true;
}

Context ctx = ActivityThread.currentActivityThread().getSystemContext();
if (BrowserUtils.isSystemBrowser(ctx, callerPkgName)) {
return true;
}

return false;
}

private static boolean isPermissionEligibleForChecking(@NonNull String permission) {
return switch (permission) {
case Manifest.permission.INTERNET -> true;
default -> false;
};
}

private static boolean canAccessForDebuggingPurposes(@NonNull String packageName) {
if (!Build.IS_DEBUGGABLE) {
return false;
}

String testPkgs = SystemProperties.get("persist.launchedFromPackagePermission_test_pkgs");
return ArrayUtils.contains(testPkgs.split(","), packageName);
}

@PackageManager.PermissionResult
static int checkLaunchedFromPermission(@Nullable ActivityRecord r, @NonNull String permission,
int deviceId) {
if (r == null) {
Slog.w(TAG, "Treating null previous activity record as permission denied");
return PackageManager.PERMISSION_DENIED;
}

// Fields acquired from ActivityRecord are final, no need to hold the global wm lock.
final String launchedFromPkgName = r.launchedFromPackage;
final int launchedFromUid = r.launchedFromUid;
final int userId = UserHandle.getUserId(launchedFromUid);
var permService = LocalServices.getService(PermissionManagerServiceInternal.class);

// Do not take into account of calling app's package visibility towards launchedFromPackage.
long token = Binder.clearCallingIdentity();
try {
return permService.checkPermission(launchedFromPkgName, permission, deviceId, userId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}

0 comments on commit 9351c45

Please sign in to comment.