From 7c74f78a85283912d7239214024ccca702622f21 Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Thu, 4 Jun 2015 13:01:42 -0400 Subject: Zen: New user flow for requesting DND access. - User flow is now similar to requesting access to notification content, namely prompting the user to visit a settings page for enabling/disabling apps access. - New ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED intent for apps to listen to this state change. - Removed obsolete request method and associated internal callback aidl. - Added new android.permission.ACCESS_NOTIFICATION_POLICY permission for apps to include as a signal that they want to request this access (and therefore appear in the list on the settings page). - Improve javadocs, outline the user flow in NotificationManager#isNotificationPolicyAccessGranted and link to this method elsewhere. - NoManService now persists the user-enabled package list across reboots and does so per-user. - Rename public settings intent to correspond with the noman api. Bug: 21621663 Change-Id: I72cbc21cd736e6a157b6be5d1d0ba0b4a8e7ef4e --- Android.mk | 1 - api/current.txt | 11 +- api/system-current.txt | 11 +- core/java/android/app/INotificationManager.aidl | 2 - .../android/app/INotificationManagerCallback.aidl | 24 ----- core/java/android/app/NotificationManager.java | 77 +++++--------- core/java/android/provider/Settings.java | 18 +++- core/res/AndroidManifest.xml | 6 ++ core/res/res/values/strings.xml | 5 + .../notification/NotificationManagerService.java | 111 ++++++++++++++------- 10 files changed, 133 insertions(+), 133 deletions(-) delete mode 100644 core/java/android/app/INotificationManagerCallback.aidl diff --git a/Android.mk b/Android.mk index d6dac53..ade8536 100644 --- a/Android.mk +++ b/Android.mk @@ -74,7 +74,6 @@ LOCAL_SRC_FILES += \ core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ core/java/android/app/INotificationManager.aidl \ - core/java/android/app/INotificationManagerCallback.aidl \ core/java/android/app/IProcessObserver.aidl \ core/java/android/app/ISearchManager.aidl \ core/java/android/app/ISearchManagerCallback.aidl \ diff --git a/api/current.txt b/api/current.txt index e1c83e5..68637b3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12,6 +12,7 @@ package android { field public static final java.lang.String ACCESS_LOCATION_EXTRA_COMMANDS = "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"; field public static final java.lang.String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION"; field public static final java.lang.String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE"; + field public static final java.lang.String ACCESS_NOTIFICATION_POLICY = "android.permission.ACCESS_NOTIFICATION_POLICY"; field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER"; field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE"; field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER"; @@ -5122,10 +5123,10 @@ package android.app { method public boolean isNotificationPolicyAccessGranted(); method public void notify(int, android.app.Notification); method public void notify(java.lang.String, int, android.app.Notification); - method public void requestPolicyAccess(android.app.NotificationManager.NotificationPolicyAccessRequestCallback, android.os.Handler); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; + field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED"; field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4 field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1 @@ -5134,12 +5135,6 @@ package android.app { field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0 } - public static abstract class NotificationManager.NotificationPolicyAccessRequestCallback { - ctor public NotificationManager.NotificationPolicyAccessRequestCallback(); - method public abstract void onAccessDenied(); - method public abstract void onAccessGranted(); - } - public static class NotificationManager.Policy implements android.os.Parcelable { ctor public NotificationManager.Policy(int, int, int); method public int describeContents(); @@ -26503,6 +26498,7 @@ package android.provider { field public static final java.lang.String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS"; field public static final java.lang.String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS"; field public static final java.lang.String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; + field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS"; field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS"; field public static final java.lang.String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS"; field public static final java.lang.String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; @@ -26521,7 +26517,6 @@ package android.provider { field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS"; field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS"; field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS"; - field public static final java.lang.String ACTION_ZEN_ACCESS_SETTINGS = "android.settings.ZEN_ACCESS_SETTINGS"; field public static final java.lang.String AUTHORITY = "settings"; field public static final java.lang.String EXTRA_ACCOUNT_TYPES = "account_types"; field public static final java.lang.String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled"; diff --git a/api/system-current.txt b/api/system-current.txt index 2839f68..ff9c861 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -18,6 +18,7 @@ package android { field public static final java.lang.String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS"; field public static final java.lang.String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE"; field public static final java.lang.String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS"; + field public static final java.lang.String ACCESS_NOTIFICATION_POLICY = "android.permission.ACCESS_NOTIFICATION_POLICY"; field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER"; field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE"; field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER"; @@ -5218,10 +5219,10 @@ package android.app { method public boolean isNotificationPolicyAccessGranted(); method public void notify(int, android.app.Notification); method public void notify(java.lang.String, int, android.app.Notification); - method public void requestPolicyAccess(android.app.NotificationManager.NotificationPolicyAccessRequestCallback, android.os.Handler); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; + field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED"; field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4 field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1 @@ -5230,12 +5231,6 @@ package android.app { field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0 } - public static abstract class NotificationManager.NotificationPolicyAccessRequestCallback { - ctor public NotificationManager.NotificationPolicyAccessRequestCallback(); - method public abstract void onAccessDenied(); - method public abstract void onAccessGranted(); - } - public static class NotificationManager.Policy implements android.os.Parcelable { ctor public NotificationManager.Policy(int, int, int); method public int describeContents(); @@ -28535,6 +28530,7 @@ package android.provider { field public static final java.lang.String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS"; field public static final java.lang.String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS"; field public static final java.lang.String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; + field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS"; field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS"; field public static final java.lang.String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS"; field public static final java.lang.String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; @@ -28553,7 +28549,6 @@ package android.provider { field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS"; field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS"; field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS"; - field public static final java.lang.String ACTION_ZEN_ACCESS_SETTINGS = "android.settings.ZEN_ACCESS_SETTINGS"; field public static final java.lang.String AUTHORITY = "settings"; field public static final java.lang.String EXTRA_ACCOUNT_TYPES = "account_types"; field public static final java.lang.String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled"; diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 63ff005..f78fb47 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -17,7 +17,6 @@ package android.app; -import android.app.INotificationManagerCallback; import android.app.ITransientNotification; import android.app.Notification; import android.app.NotificationManager; @@ -87,7 +86,6 @@ interface INotificationManager oneway void setZenMode(int mode, in Uri conditionId, String reason); oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions); oneway void requestZenModeConditions(in IConditionListener callback, int relevance); - oneway void requestNotificationPolicyAccess(String pkg, in INotificationManagerCallback callback); boolean isNotificationPolicyAccessGranted(String pkg); NotificationManager.Policy getNotificationPolicy(String pkg); void setNotificationPolicy(String pkg, in NotificationManager.Policy policy); diff --git a/core/java/android/app/INotificationManagerCallback.aidl b/core/java/android/app/INotificationManagerCallback.aidl deleted file mode 100644 index 9929745..0000000 --- a/core/java/android/app/INotificationManagerCallback.aidl +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2015, 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.app; - -import android.app.NotificationManager; - -/** @hide */ -oneway interface INotificationManagerCallback { - void onPolicyRequestResult(boolean granted); -} diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 557964b..0904e21 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -101,6 +101,16 @@ public class NotificationManager = "android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED"; /** + * Intent that is broadcast when the state of {@link #isNotificationPolicyAccessGranted()} + * changes. + * + * This broadcast is only sent to registered receivers, and only to the apps that have changed. + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED + = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; + + /** * Intent that is broadcast when the state of getNotificationPolicy() changes. * This broadcast is only sent to registered receivers. */ @@ -403,55 +413,18 @@ public class NotificationManager } /** - * Requests the ability to read/modify notification policy for the calling package. - * - * @param callback required, used to receive the granted or the denied signal. - * @param handler The handler used when receiving the result. - * If null, the current thread is used. - */ - public void requestPolicyAccess(@NonNull final NotificationPolicyAccessRequestCallback callback, - @Nullable Handler handler) { - checkRequired("callback", callback); - final Handler h = handler != null ? handler : new Handler(); - INotificationManager service = getService(); - try { - service.requestNotificationPolicyAccess(mContext.getOpPackageName(), - new INotificationManagerCallback.Stub() { - @Override - public void onPolicyRequestResult(final boolean granted) throws RemoteException { - h.post(new Runnable() { - @Override - public void run() { - if (granted) { - callback.onAccessGranted(); - } else { - callback.onAccessDenied(); - } - } - }); - } - }); - } catch (RemoteException e) { - } - } - - /** Callback for receiving the result of a policy access request. */ - public static abstract class NotificationPolicyAccessRequestCallback { - /** - * Received if the request was granted for this package. - */ - public abstract void onAccessGranted(); - - /** - * Received if the request was denied for this package. - */ - public abstract void onAccessDenied(); - } - - /** * Checks the ability to read/modify notification policy for the calling package. * + *

* Returns true if the calling package can read/modify notification policy. + * + *

+ * Request policy access by sending the user to the activity that matches the system intent + * action {@link android.provider.Settings#ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS}. + * + *

+ * Use {@link #ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED} to listen for + * user grant or denial of this access. */ public boolean isNotificationPolicyAccessGranted() { INotificationManager service = getService(); @@ -476,7 +449,8 @@ public class NotificationManager * Gets the current notification policy. * *

- * Only available if policy access is granted. + * Only available if policy access is granted to this package. + * See {@link #isNotificationPolicyAccessGranted}. */ public Policy getNotificationPolicy() { INotificationManager service = getService(); @@ -491,7 +465,8 @@ public class NotificationManager * Sets the current notification policy. * *

- * Only available if policy access is granted. + * Only available if policy access is granted to this package. + * See {@link #isNotificationPolicyAccessGranted}. * * @param policy The new desired policy. */ @@ -716,7 +691,8 @@ public class NotificationManager * unavailable. * *

- * Only available if policy access is granted. + * Only available if policy access is granted to this package. + * See {@link #isNotificationPolicyAccessGranted}. */ public final int getCurrentInterruptionFilter() { final INotificationManager service = getService(); @@ -738,7 +714,8 @@ public class NotificationManager * unavailable. * *

- * Only available if policy access is granted. + * Only available if policy access is granted to this package. + * See {@link #isNotificationPolicyAccessGranted}. */ public final void setInterruptionFilter(int interruptionFilter) { final INotificationManager service = getService(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 37645b5..cac4a53 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -811,14 +811,17 @@ public final class Settings { /** * Activity Action: Show Do Not Disturb access settings. *

- * In some cases, a matching Activity may not exist, so ensure you safeguard against this. + * Users can grant and deny access to Do Not Disturb configuration from here. + * See {@link android.app.NotificationManager#isNotificationPolicyAccessGranted()} for more + * details. *

* Input: Nothing. *

* Output: Nothing. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_ZEN_ACCESS_SETTINGS = "android.settings.ZEN_ACCESS_SETTINGS"; + public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS + = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS"; /** * @hide @@ -5425,7 +5428,7 @@ public final class Settings { public static final String ASSIST_STRUCTURE_ENABLED = "assist_structure_enabled"; /** - * Names of the packages that the current user has explicitly allowed to + * Names of the service components that the current user has explicitly allowed to * see all of the user's notifications, separated by ':'. * * @hide @@ -5433,6 +5436,15 @@ public final class Settings { public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners"; /** + * Names of the packages that the current user has explicitly allowed to + * manage notification policy configuration, separated by ':'. + * + * @hide + */ + public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = + "enabled_notification_policy_access_packages"; + + /** * @hide */ public static final String ENABLED_CONDITION_PROVIDERS = "enabled_condition_providers"; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 439affe..9fc5f13 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2327,6 +2327,12 @@ + + + Allows the holder to bind to carrier services. Should never be needed for normal apps. + + access Do Not Disturb + + Allows the app to read and write Do Not Disturb configuration. + diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 2d15d13..f12cee2 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -29,7 +29,6 @@ import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.INotificationManager; -import android.app.INotificationManagerCallback; import android.app.ITransientNotification; import android.app.Notification; import android.app.NotificationManager; @@ -130,6 +129,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map.Entry; import java.util.Objects; @@ -237,8 +237,7 @@ public class NotificationManagerService extends SystemService { new ArrayMap(); final ArrayList mToastQueue = new ArrayList(); final ArrayMap mSummaryByGroupKey = new ArrayMap<>(); - private final ArrayMap mPolicyAccess = new ArrayMap<>(); - + final PolicyAccess mPolicyAccess = new PolicyAccess(); // The last key in this list owns the hardware. ArrayList mLights = new ArrayList<>(); @@ -787,7 +786,7 @@ public class NotificationManagerService extends SystemService { } }; - class SettingsObserver extends ContentObserver { + private final class SettingsObserver extends ContentObserver { private final Uri NOTIFICATION_LIGHT_PULSE_URI = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE); @@ -1641,7 +1640,7 @@ public class NotificationManagerService extends SystemService { } private boolean checkPackagePolicyAccess(String pkg) { - return Boolean.TRUE.equals(mPolicyAccess.get(pkg)); + return mPolicyAccess.isPackageGranted(pkg); } private boolean checkPolicyAccess(String pkg) { @@ -1724,31 +1723,6 @@ public class NotificationManagerService extends SystemService { } @Override - public void requestNotificationPolicyAccess(String pkg, - INotificationManagerCallback callback) throws RemoteException { - if (callback == null) { - Slog.w(TAG, "requestNotificationPolicyAccess: no callback specified"); - return; - } - if (pkg == null) { - Slog.w(TAG, "requestNotificationPolicyAccess denied: no package specified"); - callback.onPolicyRequestResult(false); - return; - } - final long identity = Binder.clearCallingIdentity(); - try { - synchronized (mNotificationList) { - // immediately grant for now - mPolicyAccess.put(pkg, true); - if (DBG) Slog.w(TAG, "requestNotificationPolicyAccess granted for " + pkg); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - callback.onPolicyRequestResult(true); - } - - @Override public boolean isNotificationPolicyAccessGranted(String pkg) { return checkPolicyAccess(pkg); } @@ -1765,13 +1739,7 @@ public class NotificationManagerService extends SystemService { enforceSystemOrSystemUI("request policy access packages"); final long identity = Binder.clearCallingIdentity(); try { - synchronized (mNotificationList) { - final String[] rt = new String[mPolicyAccess.size()]; - for (int i = 0; i < mPolicyAccess.size(); i++) { - rt[i] = mPolicyAccess.keyAt(i); - } - return rt; - } + return mPolicyAccess.getRequestingPackages(); } finally { Binder.restoreCallingIdentity(identity); } @@ -3518,4 +3486,73 @@ public class NotificationManagerService extends SystemService { return value; } } + + private final class PolicyAccess { + private static final String SEPARATOR = ":"; + private final String[] PERM = { + android.Manifest.permission.ACCESS_NOTIFICATION_POLICY + }; + + public boolean isPackageGranted(String pkg) { + return pkg != null && getGrantedPackages().contains(pkg); + } + + public void put(String pkg, boolean granted) { + if (pkg == null) return; + final ArraySet pkgs = getGrantedPackages(); + boolean changed; + if (granted) { + changed = pkgs.add(pkg); + } else { + changed = pkgs.remove(pkg); + } + if (!changed) return; + final String setting = TextUtils.join(SEPARATOR, pkgs); + final int currentUser = ActivityManager.getCurrentUser(); + Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES, + setting, + currentUser); + getContext().sendBroadcastAsUser(new Intent(NotificationManager + .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) + .setPackage(pkg) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null); + } + + public ArraySet getGrantedPackages() { + final ArraySet pkgs = new ArraySet<>(); + final String setting = Settings.Secure.getStringForUser( + getContext().getContentResolver(), + Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES, + ActivityManager.getCurrentUser()); + if (setting != null) { + final String[] tokens = setting.split(SEPARATOR); + for (int i = 0; i < tokens.length; i++) { + String token = tokens[i]; + if (token != null) { + token.trim(); + } + if (TextUtils.isEmpty(token)) { + continue; + } + pkgs.add(token); + } + } + return pkgs; + } + + public String[] getRequestingPackages() throws RemoteException { + final ParceledListSlice list = AppGlobals.getPackageManager() + .getPackagesHoldingPermissions(PERM, 0 /*flags*/, + ActivityManager.getCurrentUser()); + final List pkgs = list.getList(); + if (pkgs == null || pkgs.isEmpty()) return new String[0]; + final int N = pkgs.size(); + final String[] rt = new String[N]; + for (int i = 0; i < N; i++) { + rt[i] = pkgs.get(i).packageName; + } + return rt; + } + } } -- cgit v1.1