diff options
author | Eugene Susla <eugenesusla@google.com> | 2017-04-10 11:51:58 -0700 |
---|---|---|
committer | Eugene Susla <eugenesusla@google.com> | 2017-04-14 19:01:53 -0700 |
commit | cf00adebec29d4cdbec5bc0f004b26a09327c236 (patch) | |
tree | ce6d7f9ee1bfd4af46a89130f719be42acd41bc7 /services/print | |
parent | 47dea3b16ddfc536fb68a59fda7dfeef71b886f1 (diff) |
API for notification listener for Companioon apps
Test: 1. Trigger the confitrmation dialog.
Ensure it looks exactly like the one from settings.
2. Call an API without associating the appa first
Ensure exception is thrown with a message mentioning the need to associate 1st
Change-Id: I94d4116e1988db869ed445ae3fd018c50590e3f4
Diffstat (limited to 'services/print')
-rw-r--r-- | services/print/java/com/android/server/print/CompanionDeviceManagerService.java | 79 |
1 files changed, 66 insertions, 13 deletions
diff --git a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java index 6b3271ff3467..122a954b4d30 100644 --- a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java +++ b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java @@ -20,10 +20,12 @@ package com.android.server.print; import static com.android.internal.util.CollectionUtils.size; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.internal.util.Preconditions.checkState; import android.Manifest; import android.annotation.CheckResult; import android.annotation.Nullable; +import android.app.PendingIntent; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; import android.companion.ICompanionDeviceDiscoveryService; @@ -47,13 +49,18 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.provider.Settings; +import android.provider.SettingsStringUtil.ComponentNameSet; +import android.text.BidiFormatter; import android.util.AtomicFile; import android.util.ExceptionUtils; +import android.util.Log; import android.util.Slog; import android.util.Xml; import com.android.internal.app.IAppOpsService; import com.android.internal.content.PackageMonitor; +import com.android.internal.notification.NotificationAccessConfirmationActivityContract; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.server.FgThread; @@ -80,6 +87,7 @@ import java.util.function.Function; //TODO schedule stopScan on activity destroy(except if configuration change) //TODO on associate called again after configuration change -> replace old callback with new //TODO avoid leaking calling activity in IFindDeviceCallback (see PrintManager#print for example) +//TODO check user-feature present in manifest on API calls /** @hide */ public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient { @@ -217,6 +225,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind a -> a.deviceAddress); } + //TODO also revoke notification access @Override public void disassociate(String deviceMacAddress, String callingPackage) throws RemoteException { @@ -237,11 +246,49 @@ public class CompanionDeviceManagerService extends SystemService implements Bind checkArgument(getCallingUserId() == userId, "Must be called by either same user or system"); - mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg); } + + @Override + public PendingIntent requestNotificationAccess(ComponentName component) + throws RemoteException { + String callingPackage = component.getPackageName(); + checkCanCallNotificationApi(callingPackage); + int userId = getCallingUserId(); + String packageTitle = BidiFormatter.getInstance().unicodeWrap( + getPackageInfo(callingPackage, userId) + .applicationInfo + .loadSafeLabel(getContext().getPackageManager()) + .toString()); + long identity = Binder.clearCallingIdentity(); + try { + return PendingIntent.getActivity(getContext(), + 0 /* request code */, + NotificationAccessConfirmationActivityContract.launcherIntent( + userId, component, packageTitle), + PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_CANCEL_CURRENT); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public boolean hasNotificationAccess(ComponentName component) throws RemoteException { + checkCanCallNotificationApi(component.getPackageName()); + String setting = Settings.Secure.getString(getContext().getContentResolver(), + Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + return new ComponentNameSet(setting).contains(component); + } + + private void checkCanCallNotificationApi(String callingPackage) throws RemoteException { + checkCallerIsSystemOr(callingPackage); + checkState(!ArrayUtils.isEmpty(readAllAssociations(getCallingUserId(), callingPackage)), + "App must have an association before calling this API"); + } } + private int getCallingUserId() { return UserHandle.getUserId(Binder.getCallingUid()); } @@ -291,6 +338,17 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return new ICompanionDeviceDiscoveryServiceCallback.Stub() { @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (Throwable e) { + Slog.e(LOG_TAG, "Error during IPC", e); + throw ExceptionUtils.propagate(e, RemoteException.class); + } + } + + @Override public void onDeviceSelected(String packageName, int userId, String deviceAddress) { updateSpecialAccessPermissionForAssociatedPackage(packageName, userId); recordAssociation(packageName, deviceAddress); @@ -301,7 +359,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind public void onDeviceSelectionCancel() { cleanup(); } - }; } @@ -351,8 +408,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } private void recordAssociation(String priviledgedPackage, String deviceAddress) { + if (DEBUG) { + Log.i(LOG_TAG, "recordAssociation(priviledgedPackage = " + priviledgedPackage + + ", deviceAddress = " + deviceAddress + ")"); + } + int userId = getCallingUserId(); updateAssociations(associations -> CollectionUtils.add(associations, - new Association(getCallingUserId(), deviceAddress, priviledgedPackage))); + new Association(userId, deviceAddress, priviledgedPackage))); } private void updateAssociations(Function<List<Association>, List<Association>> update) { @@ -364,7 +426,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind final AtomicFile file = getStorageFileForUser(userId); synchronized (file) { List<Association> associations = readAllAssociations(userId); - final ArrayList<Association> old = new ArrayList<>(associations); + final List<Association> old = CollectionUtils.copyOf(associations); associations = update.apply(associations); if (size(old) == size(associations)) return; @@ -394,15 +456,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind }); } - - - //TODO Show dialog before recording notification access -// final SettingStringHelper setting = -// new SettingStringHelper( -// getContext().getContentResolver(), -// Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, -// getUserId()); -// setting.write(ColonDelimitedSet.OfStrings.add(setting.read(), priviledgedPackage)); } private AtomicFile getStorageFileForUser(int uid) { |