summaryrefslogtreecommitdiff
path: root/services/print
diff options
context:
space:
mode:
authorEugene Susla <eugenesusla@google.com>2017-04-10 11:51:58 -0700
committerEugene Susla <eugenesusla@google.com>2017-04-14 19:01:53 -0700
commitcf00adebec29d4cdbec5bc0f004b26a09327c236 (patch)
treece6d7f9ee1bfd4af46a89130f719be42acd41bc7 /services/print
parent47dea3b16ddfc536fb68a59fda7dfeef71b886f1 (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.java79
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) {