diff options
-rw-r--r-- | api/current.txt | 2 | ||||
-rw-r--r-- | api/system-current.txt | 2 | ||||
-rw-r--r-- | api/test-current.txt | 2 | ||||
-rw-r--r-- | core/java/android/companion/CompanionDeviceManager.java | 43 | ||||
-rw-r--r-- | core/java/android/companion/ICompanionDeviceManager.aidl | 7 | ||||
-rw-r--r-- | core/java/android/os/Binder.java | 30 | ||||
-rw-r--r-- | core/java/android/provider/SettingsStringUtil.java | 7 | ||||
-rw-r--r-- | core/java/com/android/internal/app/AlertActivity.java | 12 | ||||
-rw-r--r-- | core/java/com/android/internal/app/AlertController.java | 5 | ||||
-rw-r--r-- | core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java | 37 | ||||
-rw-r--r-- | core/java/com/android/internal/util/CollectionUtils.java | 42 | ||||
-rw-r--r-- | core/java/com/android/internal/util/FunctionalUtils.java | 48 | ||||
-rw-r--r-- | core/java/com/android/internal/util/Preconditions.java | 17 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 5 | ||||
-rw-r--r-- | core/res/res/values/symbols.xml | 3 | ||||
-rw-r--r-- | services/print/java/com/android/server/print/CompanionDeviceManagerService.java | 79 |
16 files changed, 305 insertions, 36 deletions
diff --git a/api/current.txt b/api/current.txt index ddce8aea75b3..7f0d5b9ea1f6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8262,6 +8262,8 @@ package android.companion { method public void associate(android.companion.AssociationRequest, android.companion.CompanionDeviceManager.Callback, android.os.Handler); method public void disassociate(java.lang.String); method public java.util.List<java.lang.String> getAssociations(); + method public boolean hasNotificationAccess(android.content.ComponentName); + method public void requestNotificationAccess(android.content.ComponentName); field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE"; } diff --git a/api/system-current.txt b/api/system-current.txt index 53babc5bf28b..c0f21f2e1aa5 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -8756,6 +8756,8 @@ package android.companion { method public void associate(android.companion.AssociationRequest, android.companion.CompanionDeviceManager.Callback, android.os.Handler); method public void disassociate(java.lang.String); method public java.util.List<java.lang.String> getAssociations(); + method public boolean hasNotificationAccess(android.content.ComponentName); + method public void requestNotificationAccess(android.content.ComponentName); field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE"; } diff --git a/api/test-current.txt b/api/test-current.txt index 9b7bd5eb11fa..04354e359cef 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -8293,6 +8293,8 @@ package android.companion { method public void associate(android.companion.AssociationRequest, android.companion.CompanionDeviceManager.Callback, android.os.Handler); method public void disassociate(java.lang.String); method public java.util.List<java.lang.String> getAssociations(); + method public boolean hasNotificationAccess(android.content.ComponentName); + method public void requestNotificationAccess(android.content.ComponentName); field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE"; } diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 4d788e783f34..e50b2a97c2d2 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -22,11 +22,13 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; +import android.content.ComponentName; import android.content.Context; import android.content.IntentSender; import android.content.pm.PackageManager; import android.os.Handler; import android.os.RemoteException; +import android.service.notification.NotificationListenerService; import android.util.Log; import java.util.Collections; @@ -195,22 +197,47 @@ public final class CompanionDeviceManager { } } - /** @hide */ - public void requestNotificationAccess() { + /** + * Request notification access for the given component. + * + * The given component must follow the protocol specified in {@link NotificationListenerService} + * + * Only components from the same {@link ComponentName#getPackageName package} as the calling app + * are allowed. + * + * Your app must have an association with a device before calling this API + */ + public void requestNotificationAccess(ComponentName component) { if (!checkFeaturePresent()) { return; } - //TODO implement - throw new UnsupportedOperationException("Not yet implemented"); + try { + mService.requestNotificationAccess(component).send(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (PendingIntent.CanceledException e) { + throw new RuntimeException(e); + } } - /** @hide */ - public boolean haveNotificationAccess() { + /** + * Check whether the given component can access the notifications via a + * {@link NotificationListenerService} + * + * Your app must have an association with a device before calling this API + * + * @param component the name of the component + * @return whether the given component has the notification listener permission + */ + public boolean hasNotificationAccess(ComponentName component) { if (!checkFeaturePresent()) { return false; } - //TODO implement - throw new UnsupportedOperationException("Not yet implemented"); + try { + return mService.hasNotificationAccess(component); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } private boolean checkFeaturePresent() { diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl index 7798406c35e5..d3952084116c 100644 --- a/core/java/android/companion/ICompanionDeviceManager.aidl +++ b/core/java/android/companion/ICompanionDeviceManager.aidl @@ -16,8 +16,10 @@ package android.companion; +import android.app.PendingIntent; import android.companion.IFindDeviceCallback; import android.companion.AssociationRequest; +import android.content.ComponentName; /** * Interface for communication with the core companion device manager service. @@ -32,7 +34,6 @@ interface ICompanionDeviceManager { List<String> getAssociations(String callingPackage, int userId); void disassociate(String deviceMacAddress, String callingPackage); - //TODO add these -// boolean haveNotificationAccess(String packageName); -// oneway void requestNotificationAccess(String packageName); + boolean hasNotificationAccess(in ComponentName component); + PendingIntent requestNotificationAccess(in ComponentName component); } diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 15bd175949c4..ff0bc69ed325 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -16,9 +16,15 @@ package android.os; +import android.util.ExceptionUtils; import android.util.Log; import android.util.Slog; + import com.android.internal.util.FastPrintWriter; +import com.android.internal.util.FunctionalUtils; +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; +import com.android.internal.util.FunctionalUtils.ThrowingSupplier; + import libcore.io.IoUtils; import java.io.FileDescriptor; @@ -26,7 +32,6 @@ import java.io.FileOutputStream; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.lang.reflect.Modifier; -import java.util.function.Supplier; /** * Base class for a remotable object, the core part of a lightweight @@ -251,14 +256,23 @@ public class Binder implements IBinder { * Convenience method for running the provided action enclosed in * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} * + * Any exception thrown by the given action will be caught and rethrown after the call to + * {@link #restoreCallingIdentity} + * * @hide */ - public static final void withCleanCallingIdentity(Runnable action) { + public static final void withCleanCallingIdentity(ThrowingRunnable action) { long callingIdentity = clearCallingIdentity(); + Throwable throwableToPropagate = null; try { action.run(); + } catch (Throwable throwable) { + throwableToPropagate = throwable; } finally { restoreCallingIdentity(callingIdentity); + if (throwableToPropagate != null) { + throw ExceptionUtils.propagate(throwableToPropagate); + } } } @@ -266,14 +280,24 @@ public class Binder implements IBinder { * Convenience method for running the provided action enclosed in * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result * + * Any exception thrown by the given action will be caught and rethrown after the call to + * {@link #restoreCallingIdentity} + * * @hide */ - public static final <T> T withCleanCallingIdentity(Supplier<T> action) { + public static final <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) { long callingIdentity = clearCallingIdentity(); + Throwable throwableToPropagate = null; try { return action.get(); + } catch (Throwable throwable) { + throwableToPropagate = throwable; + return null; // overridden by throwing in finally block } finally { restoreCallingIdentity(callingIdentity); + if (throwableToPropagate != null) { + throw ExceptionUtils.propagate(throwableToPropagate); + } } } diff --git a/core/java/android/provider/SettingsStringUtil.java b/core/java/android/provider/SettingsStringUtil.java index 3dfedea18323..a3dc9471a1d0 100644 --- a/core/java/android/provider/SettingsStringUtil.java +++ b/core/java/android/provider/SettingsStringUtil.java @@ -23,6 +23,7 @@ import android.text.TextUtils; import com.android.internal.util.ArrayUtils; +import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.function.Function; @@ -80,6 +81,12 @@ public class SettingsStringUtil { return s; } + public static String addAll(String delimitedElements, Collection<String> elements) { + final ColonDelimitedSet<String> set + = new ColonDelimitedSet.OfStrings(delimitedElements); + return set.addAll(elements) ? set.toString() : delimitedElements; + } + public static String add(String delimitedElements, String element) { final ColonDelimitedSet<String> set = new ColonDelimitedSet.OfStrings(delimitedElements); diff --git a/core/java/com/android/internal/app/AlertActivity.java b/core/java/com/android/internal/app/AlertActivity.java index 35ffa71de56f..999a908251dd 100644 --- a/core/java/com/android/internal/app/AlertActivity.java +++ b/core/java/com/android/internal/app/AlertActivity.java @@ -67,10 +67,15 @@ public abstract class AlertActivity extends Activity implements DialogInterface @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + return dispatchPopulateAccessibilityEvent(this, event); + } + + public static boolean dispatchPopulateAccessibilityEvent(Activity act, + AccessibilityEvent event) { event.setClassName(Dialog.class.getName()); - event.setPackageName(getPackageName()); + event.setPackageName(act.getPackageName()); - ViewGroup.LayoutParams params = getWindow().getAttributes(); + ViewGroup.LayoutParams params = act.getWindow().getAttributes(); boolean isFullScreen = (params.width == ViewGroup.LayoutParams.MATCH_PARENT) && (params.height == ViewGroup.LayoutParams.MATCH_PARENT); event.setFullScreen(isFullScreen); @@ -86,8 +91,7 @@ public abstract class AlertActivity extends Activity implements DialogInterface * @see #mAlertParams */ protected void setupAlert() { - mAlertParams.apply(mAlert); - mAlert.installContent(); + mAlert.installContent(mAlertParams); } @Override diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 95c291a9e8fe..46cb5461b682 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -247,6 +247,11 @@ public class AlertController { return false; } + public void installContent(AlertParams params) { + params.apply(this); + installContent(); + } + public void installContent() { int contentView = selectContentView(); mWindow.setContentView(contentView); diff --git a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java new file mode 100644 index 000000000000..4ce6f609ef73 --- /dev/null +++ b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 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 com.android.internal.notification; + +import android.content.ComponentName; +import android.content.Intent; + +public final class NotificationAccessConfirmationActivityContract { + private static final ComponentName COMPONENT_NAME = new ComponentName( + "com.android.settings", + "com.android.settings.notification.NotificationAccessConfirmationActivity"); + public static final String EXTRA_USER_ID = "user_id"; + public static final String EXTRA_COMPONENT_NAME = "component_name"; + public static final String EXTRA_PACKAGE_TITLE = "package_title"; + + public static Intent launcherIntent(int userId, ComponentName component, String packageTitle) { + return new Intent() + .setComponent(COMPONENT_NAME) + .putExtra(EXTRA_USER_ID, userId) + .putExtra(EXTRA_COMPONENT_NAME, component) + .putExtra(EXTRA_PACKAGE_TITLE, packageTitle); + } +} diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java index 183945ca1382..96b443d28abc 100644 --- a/core/java/com/android/internal/util/CollectionUtils.java +++ b/core/java/com/android/internal/util/CollectionUtils.java @@ -16,6 +16,8 @@ package com.android.internal.util; +import static com.android.internal.util.ArrayUtils.isEmpty; + import android.annotation.NonNull; import android.annotation.Nullable; @@ -64,7 +66,7 @@ public class CollectionUtils { */ public static @NonNull <I, O> List<O> map(@Nullable List<I> cur, Function<? super I, ? extends O> f) { - if (cur == null || cur.isEmpty()) return Collections.emptyList(); + if (isEmpty(cur)) return Collections.emptyList(); final ArrayList<O> result = new ArrayList<>(); for (int i = 0; i < cur.size(); i++) { result.add(f.apply(cur.get(i))); @@ -73,6 +75,30 @@ public class CollectionUtils { } /** + * {@link #map(List, Function)} + {@link #filter(List, java.util.function.Predicate)} + * + * Calling this is equivalent (but more memory efficient) to: + * + * {@code + * filter( + * map(cur, f), + * i -> { i != null }) + * } + */ + public static @NonNull <I, O> List<O> mapNotNull(@Nullable List<I> cur, + Function<? super I, ? extends O> f) { + if (isEmpty(cur)) return Collections.emptyList(); + final ArrayList<O> result = new ArrayList<>(); + for (int i = 0; i < cur.size(); i++) { + O transformed = f.apply(cur.get(i)); + if (transformed != null) { + result.add(transformed); + } + } + return result; + } + + /** * Returns the given list, or an immutable empty list if the provided list is null * * This can be used to guaranty null-safety without paying the price of extra allocations @@ -94,7 +120,7 @@ public class CollectionUtils { * Returns the elements of the given list that are of type {@code c} */ public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) { - if (ArrayUtils.isEmpty(list)) return Collections.emptyList(); + if (isEmpty(list)) return Collections.emptyList(); ArrayList<T> result = null; for (int i = 0; i < list.size(); i++) { final Object item = list.get(i); @@ -120,7 +146,7 @@ public class CollectionUtils { */ public static @Nullable <T> T find(@Nullable List<T> items, java.util.function.Predicate<T> predicate) { - if (ArrayUtils.isEmpty(items)) return null; + if (isEmpty(items)) return null; for (int i = 0; i < items.size(); i++) { final T item = items.get(i); if (predicate.test(item)) return item; @@ -145,11 +171,17 @@ public class CollectionUtils { * {@link Collections#emptyList} */ public static @NonNull <T> List<T> remove(@Nullable List<T> cur, T val) { - if (cur == null || cur == Collections.emptyList()) { - return Collections.emptyList(); + if (isEmpty(cur)) { + return emptyIfNull(cur); } cur.remove(val); return cur; } + /** + * @return a list that will not be affected by mutations to the given original list. + */ + public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) { + return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur); + } } diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java new file mode 100644 index 000000000000..9aeb0415b5fc --- /dev/null +++ b/core/java/com/android/internal/util/FunctionalUtils.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 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 com.android.internal.util; + +import java.util.function.Supplier; + +/** + * Utilities specific to functional programming + */ +public class FunctionalUtils { + private FunctionalUtils() {} + + /** + * An equivalent of {@link Runnable} that allows throwing checked exceptions + * + * This can be used to specify a lambda argument without forcing all the checked exceptions + * to be handled within it + */ + @FunctionalInterface + public interface ThrowingRunnable { + void run() throws Exception; + } + + /** + * An equivalent of {@link Supplier} that allows throwing checked exceptions + * + * This can be used to specify a lambda argument without forcing all the checked exceptions + * to be handled within it + */ + @FunctionalInterface + public interface ThrowingSupplier<T> { + T get() throws Exception; + } +} diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index 4e6857a72b43..e5d571672ce2 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -49,6 +49,23 @@ public class Preconditions { } /** + * Ensures that an expression checking an argument is true. + * + * @param expression the expression to check + * @param messageTemplate a printf-style message template to use if the check fails; will + * be converted to a string using {@link String#format(String, Object...)} + * @param messageArgs arguments for {@code messageTemplate} + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression, + final String messageTemplate, + final Object... messageArgs) { + if (!expression) { + throw new IllegalArgumentException(String.format(messageTemplate, messageArgs)); + } + } + + /** * Ensures that an string reference passed as a parameter to the calling * method is not empty. * diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 000c8c434ded..8869593c2194 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3556,6 +3556,11 @@ android:process=":ui"> </activity> + <activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity" + android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" + android:excludeFromRecents="true"> + </activity> + <receiver android:name="com.android.server.BootReceiver" android:systemUserOnly="true"> <intent-filter android:priority="1000"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 603e3769e75c..74cbe27a75ad 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -19,6 +19,9 @@ <!-- Private symbols that we need to reference from framework code. See frameworks/base/core/res/MakeJavaSymbols.sed for how to easily generate this. + + Can be referenced in java code as: com.android.internal.R.<type>.<name> + and in layout xml as: "@*android:<type>/<name>" --> <java-symbol type="id" name="account_name" /> <java-symbol type="id" name="account_row_icon" /> 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) { |