Cleanup of the PackageInstaller API - Frameworks
The PackageInstaller app manages side-loading apps as well
as permission management. It should be updatable, hence
should rely on system APIs to talk to the platform. This
is the first step of defining an API boundary.
Change-Id: I9814eafd0b22ae03b4b847a7007cdbf14c9e5466
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7edc6b7..d440017 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3110,6 +3110,7 @@
*
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.KILL_UID)
public void killUid(int uid, String reason) {
try {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 64586a6..783c37d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1256,7 +1256,16 @@
}
}
- /** @hide */
+ /**
+ * Sets given app op in the specified mode for app ops in the UID.
+ * This applies to all apps currently in the UID or installed in
+ * this UID in the future.
+ *
+ * @param code The app op.
+ * @param uid The UID for which to set the app.
+ * @param mode The app op mode to set.
+ * @hide
+ */
public void setUidMode(int code, int uid, int mode) {
try {
mService.setUidMode(code, uid, mode);
@@ -1265,6 +1274,25 @@
}
}
+ /**
+ * Sets given app op in the specified mode for app ops in the UID.
+ * This applies to all apps currently in the UID or installed in
+ * this UID in the future.
+ *
+ * @param appOp The app op.
+ * @param uid The UID for which to set the app.
+ * @param mode The app op mode to set.
+ * @hide
+ */
+ @SystemApi
+ public void setUidMode(String appOp, int uid, int mode) {
+ try {
+ mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public void setUserRestriction(int code, boolean restricted, IBinder token) {
setUserRestriction(code, restricted, token, /*exceptionPackages*/null);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8d29dac..47abd2b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1561,6 +1561,7 @@
* {@link} #ACTION_VIEW} to indicate the uid of the package that initiated the install
* @hide
*/
+ @SystemApi
public static final String EXTRA_ORIGINATING_UID
= "android.intent.extra.ORIGINATING_UID";
@@ -1634,6 +1635,7 @@
*
* @hide
*/
+ @SystemApi
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MANAGE_APP_PERMISSIONS =
"android.intent.action.MANAGE_APP_PERMISSIONS";
@@ -1649,6 +1651,7 @@
*
* @hide
*/
+ @SystemApi
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MANAGE_PERMISSIONS =
"android.intent.action.MANAGE_PERMISSIONS";
@@ -1686,6 +1689,7 @@
*
* @hide
*/
+ @SystemApi
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_REVIEW_PERMISSIONS =
"android.intent.action.REVIEW_PERMISSIONS";
@@ -1698,6 +1702,7 @@
*
* @hide
*/
+ @SystemApi
public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
/**
@@ -1717,102 +1722,10 @@
*
* @hide
*/
+ @SystemApi
public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
/**
- * Broadcast action that requests current permission granted information. It will respond
- * to the request by sending a broadcast with action defined by
- * {@link #EXTRA_GET_PERMISSIONS_RESPONSE_INTENT}. The response will contain
- * {@link #EXTRA_GET_PERMISSIONS_COUNT_RESULT}, as well as
- * {@link #EXTRA_GET_PERMISSIONS_GROUP_LIST_RESULT}, with contents described below or
- * a null upon failure.
- *
- * <p>If {@link #EXTRA_PACKAGE_NAME} is included then the number of permissions granted, the
- * number of permissions requested and the number of granted additional permissions
- * by that package will be calculated and included as the first
- * and second elements respectively of an int[] in the response as
- * {@link #EXTRA_GET_PERMISSIONS_COUNT_RESULT}. The response will also deliver the list
- * of localized permission group names that are granted in
- * {@link #EXTRA_GET_PERMISSIONS_GROUP_LIST_RESULT}.
- *
- * <p>If {@link #EXTRA_PACKAGE_NAME} is not included then the number of apps granted any runtime
- * permissions and the total number of apps requesting runtime permissions will be the first
- * and second elements respectively of an int[] in the response as
- * {@link #EXTRA_GET_PERMISSIONS_COUNT_RESULT}.
- *
- * @hide
- */
- public static final String ACTION_GET_PERMISSIONS_COUNT
- = "android.intent.action.GET_PERMISSIONS_COUNT";
-
- /**
- * Broadcast action that requests list of all apps that have runtime permissions. It will
- * respond to the request by sending a broadcast with action defined by
- * {@link #EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT}. The response will contain
- * {@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT}, as well as
- * {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT}, with contents described below or
- * a null upon failure.
- *
- * <p>{@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT} will contain a list of package names of
- * apps that have runtime permissions. {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT}
- * will contain the list of app labels corresponding ot the apps in the first list.
- *
- * @hide
- */
- public static final String ACTION_GET_PERMISSIONS_PACKAGES
- = "android.intent.action.GET_PERMISSIONS_PACKAGES";
-
- /**
- * Extra included in response to {@link #ACTION_GET_PERMISSIONS_COUNT}.
- * @hide
- */
- public static final String EXTRA_GET_PERMISSIONS_COUNT_RESULT
- = "android.intent.extra.GET_PERMISSIONS_COUNT_RESULT";
-
- /**
- * List of CharSequence of localized permission group labels.
- * @hide
- */
- public static final String EXTRA_GET_PERMISSIONS_GROUP_LIST_RESULT
- = "android.intent.extra.GET_PERMISSIONS_GROUP_LIST_RESULT";
-
- /**
- * String list of apps that have one or more runtime permissions.
- * @hide
- */
- public static final String EXTRA_GET_PERMISSIONS_APP_LIST_RESULT
- = "android.intent.extra.GET_PERMISSIONS_APP_LIST_RESULT";
-
- /**
- * String list of app labels for apps that have one or more runtime permissions.
- * @hide
- */
- public static final String EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT
- = "android.intent.extra.GET_PERMISSIONS_APP_LABEL_LIST_RESULT";
-
- /**
- * Boolean list describing if the app is a system app for apps that have one or more runtime
- * permissions.
- * @hide
- */
- public static final String EXTRA_GET_PERMISSIONS_IS_SYSTEM_APP_LIST_RESULT
- = "android.intent.extra.GET_PERMISSIONS_IS_SYSTEM_APP_LIST_RESULT";
-
- /**
- * Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_COUNT} broadcasts.
- * @hide
- */
- public static final String EXTRA_GET_PERMISSIONS_RESPONSE_INTENT
- = "android.intent.extra.GET_PERMISSIONS_RESONSE_INTENT";
-
- /**
- * Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_PACKAGES} broadcasts.
- * @hide
- */
- public static final String EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT
- = "android.intent.extra.GET_PERMISSIONS_PACKAGES_RESONSE_INTENT";
-
- /**
* Activity action: Launch UI to manage which apps have a given permission.
* <p>
* Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission access
@@ -1826,6 +1739,7 @@
*
* @hide
*/
+ @SystemApi
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MANAGE_PERMISSION_APPS =
"android.intent.action.MANAGE_PERMISSION_APPS";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index cf3e893..b04cbc5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2367,6 +2367,7 @@
*
* @hide
*/
+ @SystemApi
public static final int FLAG_PERMISSION_USER_SET = 1 << 0;
/**
@@ -2376,6 +2377,7 @@
*
* @hide
*/
+ @SystemApi
public static final int FLAG_PERMISSION_USER_FIXED = 1 << 1;
/**
@@ -2385,6 +2387,7 @@
*
* @hide
*/
+ @SystemApi
public static final int FLAG_PERMISSION_POLICY_FIXED = 1 << 2;
/**
@@ -2397,6 +2400,7 @@
*
* @hide
*/
+ @SystemApi
public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 1 << 3;
/**
@@ -2405,6 +2409,7 @@
*
* @hide
*/
+ @SystemApi
public static final int FLAG_PERMISSION_SYSTEM_FIXED = 1 << 4;
/**
@@ -2415,6 +2420,7 @@
*
* @hide
*/
+ @SystemApi
public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 1 << 5;
/**
@@ -2423,6 +2429,7 @@
*
* @hide
*/
+ @SystemApi
public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 1 << 6;
/**
@@ -3124,6 +3131,7 @@
*
* @hide
*/
+ @SystemApi
public abstract List<PackageInfo> getInstalledPackagesAsUser(@PackageInfoFlags int flags,
@UserIdInt int userId);
diff --git a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
new file mode 100644
index 0000000..8766508
--- /dev/null
+++ b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.permission;
+
+import android.os.RemoteCallback;
+
+/**
+ * Interface for communication with the permission presenter service.
+ *
+ * @hide
+ */
+oneway interface IRuntimePermissionPresenter {
+ void getAppPermissions(String packageName, in RemoteCallback callback);
+ void getAppsUsingPermissions(boolean system, in RemoteCallback callback);
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl b/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl
new file mode 100644
index 0000000..f96a32f
--- /dev/null
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.permission;
+
+parcelable RuntimePermissionPresentationInfo;
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java b/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
new file mode 100644
index 0000000..352e8ad
--- /dev/null
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information about how a runtime permission
+ * is to be presented in the UI. A single runtime permission
+ * presented to the user may correspond to multiple platform defined
+ * permissions, e.g. the location permission may control both the
+ * coarse and fine platform permissions.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RuntimePermissionPresentationInfo implements Parcelable {
+ private static final int FLAG_GRANTED = 1 << 0;
+ private static final int FLAG_STANDARD = 1 << 1;
+
+ private final CharSequence mLabel;
+ private final int mFlags;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param label The permission label.
+ * @param granted Whether the permission is granted.
+ * @param standard Whether this is a platform-defined permission.
+ */
+ public RuntimePermissionPresentationInfo(CharSequence label,
+ boolean granted, boolean standard) {
+ mLabel = label;
+ int flags = 0;
+ if (granted) {
+ flags |= FLAG_GRANTED;
+ }
+ if (standard) {
+ flags |= FLAG_STANDARD;
+ }
+ mFlags = flags;
+ }
+
+ private RuntimePermissionPresentationInfo(Parcel parcel) {
+ mLabel = parcel.readCharSequence();
+ mFlags = parcel.readInt();
+ }
+
+ /**
+ * @return Whether the permission is granted.
+ */
+ public boolean isGranted() {
+ return (mFlags & FLAG_GRANTED) != 0;
+ }
+
+ /**
+ * @return Whether the permission is platform-defined.
+ */
+ public boolean isStandard() {
+ return (mFlags & FLAG_STANDARD) != 0;
+ }
+
+ /**
+ * Gets the permission label.
+ *
+ * @return The label.
+ */
+ public @NonNull CharSequence getLabel() {
+ return mLabel;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeCharSequence(mLabel);
+ parcel.writeInt(mFlags);
+ }
+
+ public static final Creator<RuntimePermissionPresentationInfo> CREATOR =
+ new Creator<RuntimePermissionPresentationInfo>() {
+ public RuntimePermissionPresentationInfo createFromParcel(Parcel source) {
+ return new RuntimePermissionPresentationInfo(source);
+ }
+
+ public RuntimePermissionPresentationInfo[] newArray(int size) {
+ return new RuntimePermissionPresentationInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
new file mode 100644
index 0000000..2e39926
--- /dev/null
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.permissionpresenterservice.RuntimePermissionPresenterService;
+import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.SomeArgs;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class provides information about runtime permissions for a specific
+ * app or all apps. This information is dedicated for presentation purposes
+ * and does not necessarily reflect the individual permissions requested/
+ * granted to an app as the platform may be grouping permissions to improve
+ * presentation and help the user make an informed choice. For example, all
+ * runtime permissions in the same permission group may be presented as a
+ * single permission in the UI.
+ *
+ * @hide
+ */
+public final class RuntimePermissionPresenter {
+ private static final String TAG = "RuntimePermPresenter";
+
+ /**
+ * The key for retrieving the result from the returned bundle.
+ *
+ * @hide
+ */
+ public static final String KEY_RESULT =
+ "android.content.pm.permission.RuntimePermissionPresenter.key.result";
+
+ /**
+ * Listener for delivering a result.
+ */
+ public static abstract class OnResultCallback {
+ /**
+ * The result for {@link #getAppPermissions(String, OnResultCallback, Handler)}.
+ * @param permissions The permissions list.
+ */
+ public void onGetAppPermissions(@NonNull
+ List<RuntimePermissionPresentationInfo> permissions) {
+ /* do nothing - stub */
+ }
+
+ /**
+ * The result for {@link #getAppsUsingPermissions(boolean, List)}.
+ * @param system Whether to return only the system apps or only the non-system ones.
+ * @param apps The apps using runtime permissions.
+ */
+ public void getAppsUsingPermissions(boolean system, @NonNull List<ApplicationInfo> apps) {
+ /* do nothing - stub */
+ }
+ }
+
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ private static RuntimePermissionPresenter sInstance;
+
+ private final RemoteService mRemoteService;
+
+ /**
+ * Gets the singleton runtime permission presenter.
+ *
+ * @param context Context for accessing resources.
+ * @return The singleton instance.
+ */
+ public static RuntimePermissionPresenter getInstance(@NonNull Context context) {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new RuntimePermissionPresenter(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+ }
+
+ private RuntimePermissionPresenter(Context context) {
+ mRemoteService = new RemoteService(context);
+ }
+
+ /**
+ * Gets the runtime permissions for an app.
+ *
+ * @param packageName The package for which to query.
+ * @param callback Callback to receive the result.
+ * @param handler Handler on which to invoke the callback.
+ */
+ public void getAppPermissions(@NonNull String packageName,
+ @NonNull OnResultCallback callback, @Nullable Handler handler) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = packageName;
+ args.arg2 = callback;
+ args.arg3 = handler;
+ Message message = mRemoteService.obtainMessage(
+ RemoteService.MSG_GET_APP_PERMISSIONS, args);
+ mRemoteService.processMessage(message);
+ }
+
+ /**
+ * Gets the system apps that use runtime permissions. System apps are ones
+ * that are considered system for presentation purposes instead of ones
+ * that are preinstalled on the system image. System apps are ones that
+ * are on the system image, haven't been updated (a.k.a factory apps)
+ * that do not have a launcher icon.
+ *
+ * @param system If true only system apps are returned otherwise only
+ * non-system ones are returned.
+ * @param callback Callback to receive the result.
+ * @param handler Handler on which to invoke the callback.
+ */
+ public void getAppsUsingPermissions(boolean system, @NonNull OnResultCallback callback,
+ @Nullable Handler handler) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callback;
+ args.arg2 = handler;
+ args.argi1 = system ? 1 : 0;
+ Message message = mRemoteService.obtainMessage(
+ RemoteService.MSG_GET_APPS_USING_PERMISSIONS, args);
+ mRemoteService.processMessage(message);
+ }
+
+ private static final class RemoteService
+ extends Handler implements ServiceConnection {
+ private static final long UNBIND_TIMEOUT_MILLIS = 10000;
+
+ public static final int MSG_GET_APP_PERMISSIONS = 1;
+ public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
+ public static final int MSG_UNBIND = 3;
+
+ private final Object mLock = new Object();
+
+ private final Context mContext;
+
+ @GuardedBy("mLock")
+ private final List<Message> mPendingWork = new ArrayList<>();
+
+ @GuardedBy("mLock")
+ private IRuntimePermissionPresenter mRemoteInstance;
+
+ @GuardedBy("mLock")
+ private boolean mBound;
+
+ public RemoteService(Context context) {
+ super(context.getMainLooper(), null, false);
+ mContext = context;
+ }
+
+ public void processMessage(Message message) {
+ synchronized (mLock) {
+ if (!mBound) {
+ Intent intent = new Intent(
+ RuntimePermissionPresenterService.SERVICE_INTERFACE);
+ intent.setPackage(mContext.getPackageManager()
+ .getPermissionControllerPackageName());
+ mBound = mContext.bindService(intent, this,
+ Context.BIND_AUTO_CREATE);
+ }
+ mPendingWork.add(message);
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ mRemoteInstance = IRuntimePermissionPresenter.Stub.asInterface(service);
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ mRemoteInstance = null;
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_GET_APP_PERMISSIONS: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ final String packageName = (String) args.arg1;
+ final OnResultCallback callback = (OnResultCallback) args.arg2;
+ final Handler handler = (Handler) args.arg3;
+ args.recycle();
+ final IRuntimePermissionPresenter remoteInstance;
+ synchronized (mLock) {
+ remoteInstance = mRemoteInstance;
+ }
+ if (remoteInstance == null) {
+ return;
+ }
+ try {
+ remoteInstance.getAppPermissions(packageName,
+ new RemoteCallback(new RemoteCallback.OnResultListener() {
+ @Override
+ public void onResult(Bundle result) {
+ final List<RuntimePermissionPresentationInfo> reportedPermissions;
+ List<RuntimePermissionPresentationInfo> permissions = null;
+ if (result != null) {
+ permissions = result.getParcelableArrayList(KEY_RESULT);
+ }
+ if (permissions == null) {
+ permissions = Collections.emptyList();
+ }
+ reportedPermissions = permissions;
+ if (handler != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onGetAppPermissions(reportedPermissions);
+ }
+ });
+ } else {
+ callback.onGetAppPermissions(reportedPermissions);
+ }
+ }
+ }, this));
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error getting app permissions", re);
+ }
+ scheduleUnbind();
+ } break;
+
+ case MSG_GET_APPS_USING_PERMISSIONS: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ final OnResultCallback callback = (OnResultCallback) args.arg1;
+ final Handler handler = (Handler) args.arg2;
+ final boolean system = args.argi1 == 1;
+ args.recycle();
+ final IRuntimePermissionPresenter remoteInstance;
+ synchronized (mLock) {
+ remoteInstance = mRemoteInstance;
+ }
+ if (remoteInstance == null) {
+ return;
+ }
+ try {
+ remoteInstance.getAppsUsingPermissions(system, new RemoteCallback(
+ new RemoteCallback.OnResultListener() {
+ @Override
+ public void onResult(Bundle result) {
+ final List<ApplicationInfo> reportedApps;
+ List<ApplicationInfo> apps = null;
+ if (result != null) {
+ apps = result.getParcelableArrayList(KEY_RESULT);
+ }
+ if (apps == null) {
+ apps = Collections.emptyList();
+ }
+ reportedApps = apps;
+ if (handler != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.getAppsUsingPermissions(system, reportedApps);
+ }
+ });
+ } else {
+ callback.getAppsUsingPermissions(system, reportedApps);
+ }
+ }
+ }, this));
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error getting apps using permissions", re);
+ }
+ scheduleUnbind();
+ } break;
+
+ case MSG_UNBIND: {
+ synchronized (mLock) {
+ if (mBound) {
+ mContext.unbindService(this);
+ mBound = false;
+ }
+ mRemoteInstance = null;
+ }
+ } break;
+ }
+
+ synchronized (mLock) {
+ scheduleNextMessageIfNeededLocked();
+ }
+ }
+
+ private void scheduleNextMessageIfNeededLocked() {
+ if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
+ Message nextMessage = mPendingWork.remove(0);
+ sendMessage(nextMessage);
+ }
+ }
+
+ private void scheduleUnbind() {
+ removeMessages(MSG_UNBIND);
+ sendEmptyMessageDelayed(MSG_UNBIND, UNBIND_TIMEOUT_MILLIS);
+ }
+ }
+}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index d0029e1..7e1fc15 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.SystemApi;
import android.text.TextUtils;
import android.util.Slog;
@@ -802,6 +803,7 @@
*
* @hide
*/
+ @SystemApi
public static final boolean PERMISSIONS_REVIEW_REQUIRED =
SystemProperties.getInt("ro.permission_review_required", 0) == 1;
diff --git a/core/java/android/os/RemoteCallback.java b/core/java/android/os/RemoteCallback.java
index 89e30a9..7dbcb95 100644
--- a/core/java/android/os/RemoteCallback.java
+++ b/core/java/android/os/RemoteCallback.java
@@ -18,10 +18,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
/**
* @hide
*/
+@SystemApi
public final class RemoteCallback implements Parcelable {
public interface OnResultListener {
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
new file mode 100644
index 0000000..405be1a
--- /dev/null
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionpresenterservice;
+
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.permission.IRuntimePermissionPresenter;
+import android.content.pm.permission.RuntimePermissionPresentationInfo;
+import android.content.pm.permission.RuntimePermissionPresenter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallback;
+import com.android.internal.os.SomeArgs;
+
+import java.util.List;
+
+/**
+ * This service presents information regarding runtime permissions that is
+ * used for presenting them in the UI. Runtime permissions are presented as
+ * a single permission in the UI but may be composed of several individual
+ * permissions.
+ *
+ * @see RuntimePermissionPresenter
+ * @see RuntimePermissionPresentationInfo
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class RuntimePermissionPresenterService extends Service {
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service
+ * in its manifest for the system to recognize it as a runtime permission
+ * presenter service.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.permissionpresenterservice.RuntimePermissionPresenterService";
+
+ // No need for locking - always set first and never modified
+ private Handler mHandler;
+
+ @Override
+ public final void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ mHandler = new MyHandler(base.getMainLooper());
+ }
+
+ /**
+ * Gets the runtime permissions for an app.
+ *
+ * @param packageName The package for which to query.
+ */
+ public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName);
+
+ /**
+ * Gets the apps that use runtime permissions.
+ *
+ * @param system Whether to return only the system apps or only the non-system ones.
+ * @return The app list.
+ */
+ public abstract List<ApplicationInfo> onGetAppsUsingPermissions(boolean system);
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return new IRuntimePermissionPresenter.Stub() {
+ @Override
+ public void getAppPermissions(String packageName, RemoteCallback callback) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = packageName;
+ args.arg2 = callback;
+ mHandler.obtainMessage(MyHandler.MSG_GET_APP_PERMISSIONS,
+ args).sendToTarget();
+ }
+
+ @Override
+ public void getAppsUsingPermissions(boolean system, RemoteCallback callback) {
+ mHandler.obtainMessage(MyHandler.MSG_GET_APPS_USING_PERMISSIONS,
+ system ? 1 : 0, 0, callback).sendToTarget();
+ }
+ };
+ }
+
+ private final class MyHandler extends Handler {
+ public static final int MSG_GET_APP_PERMISSIONS = 1;
+ public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
+
+ public MyHandler(Looper looper) {
+ super(looper, null, false);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_GET_APP_PERMISSIONS: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String packageName = (String) args.arg1;
+ RemoteCallback callback = (RemoteCallback) args.arg2;
+ args.recycle();
+ List<RuntimePermissionPresentationInfo> permissions =
+ onGetAppPermissions(packageName);
+ if (permissions != null && !permissions.isEmpty()) {
+ Bundle result = new Bundle();
+ result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT,
+ permissions);
+ callback.sendResult(result);
+ } else {
+ callback.sendResult(null);
+ }
+ } break;
+
+ case MSG_GET_APPS_USING_PERMISSIONS: {
+ RemoteCallback callback = (RemoteCallback) msg.obj;
+ final boolean system = msg.arg1 == 1;
+ List<ApplicationInfo> apps = onGetAppsUsingPermissions(system);
+ if (apps != null && !apps.isEmpty()) {
+ Bundle result = new Bundle();
+ result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, apps);
+ callback.sendResult(result);
+ } else {
+ callback.sendResult(null);
+ }
+ } break;
+ }
+ }
+ }
+}
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index a12b15e..1a1680a 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -16,6 +16,7 @@
*/
package android.widget;
+import android.annotation.SystemApi;
import android.os.UserHandle;
import com.android.internal.R;
@@ -80,6 +81,7 @@
private final CharSequence mNewPermPrefix;
private String mPackageName;
+ /** @hide */
static class MyPermissionGroupInfo extends PermissionGroupInfo {
CharSequence mLabel;
@@ -104,6 +106,7 @@
}
}
+ /** @hide */
private static class MyPermissionInfo extends PermissionInfo {
CharSequence mLabel;
@@ -128,6 +131,7 @@
}
}
+ /** @hide */
public static class PermissionItemView extends LinearLayout implements View.OnClickListener {
MyPermissionGroupInfo mGroup;
MyPermissionInfo mPerm;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0ef72cd7..acf9675 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1587,9 +1587,10 @@
<permission android:name="android.permission.INTERACT_ACROSS_USERS"
android:protectionLevel="signature|privileged|development" />
- <!-- @hide Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+ <!-- @SystemApi Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
that removes restrictions on where broadcasts can be sent and allows other
- types of interactions. -->
+ types of interactions
+ @hide -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
android:protectionLevel="signature|installer" />
@@ -2082,11 +2083,12 @@
android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi Allows an application to update application operation statistics. Not for
- use by third party apps. @hide -->
+ use by third party apps.
+ @hide -->
<permission android:name="android.permission.UPDATE_APP_OPS_STATS"
android:protectionLevel="signature|privileged|installer" />
- <!-- Allows an application to update the user app op restrictions.
+ <!-- @SystemApi Allows an application to update the user app op restrictions.
Not for use by third party apps.
@hide -->
<permission android:name="android.permission.MANAGE_APP_OPS_RESTRICTIONS"
@@ -2232,7 +2234,13 @@
<permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
android:protectionLevel="signature" />
- <!-- Must be required by a TextService (eg SpellCheckerService)
+ <!-- @SystemApi Must be required by the RuntimePermissionPresenterService to ensure
+ that only the system can bind to it.
+ @hide -->
+ <permission android:name="android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a TextService (e.g. SpellCheckerService)
to ensure that only the system can bind to it.
<p>Protection level: signature
-->
@@ -2413,21 +2421,19 @@
<permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to grant specific permissions.
+ <!-- @SystemApi Allows an application to grant specific permissions.
@hide -->
<permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"
android:protectionLevel="signature|installer|verifier" />
- <!-- Allows an app that has this permission and the permissions to install packages
+ <!-- @SystemApi Allows an app that has this permission and the permissions to install packages
to request certain runtime permissions to be granted at installation.
- @hide
- @SystemApi -->
+ @hide -->
<permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"
android:protectionLevel="signature|installer|verifier" />
- <!-- Allows an application to revoke specific permissions.
- @hide
- @SystemApi -->
+ <!-- @SystemApi Allows an application to revoke specific permissions.
+ @hide -->
<permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
android:protectionLevel="signature|installer|verifier" />
@@ -2963,7 +2969,7 @@
<permission android:name="android.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT"
android:protectionLevel="signature" />
- <!-- Allows applications to kill UIDs.
+ <!-- @SystemApi Allows applications to kill UIDs.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.KILL_UID"