diff options
9 files changed, 268 insertions, 95 deletions
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 56f5d4483270..bb844a327168 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -23,6 +23,7 @@ import android.os.Parcelable; import android.provider.OneTimeUseBuilder; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import java.util.ArrayList; import java.util.List; @@ -47,7 +48,7 @@ public final class AssociationRequest implements Parcelable { private AssociationRequest( boolean singleDevice, @Nullable List<DeviceFilter<?>> deviceFilters) { this.mSingleDevice = singleDevice; - this.mDeviceFilters = ArrayUtils.emptyIfNull(deviceFilters); + this.mDeviceFilters = CollectionUtils.emptyIfNull(deviceFilters); } private AssociationRequest(Parcel in) { diff --git a/core/java/android/companion/BluetoothDeviceFilter.java b/core/java/android/companion/BluetoothDeviceFilter.java index 0f16b7b90165..1d8df7f26f6e 100644 --- a/core/java/android/companion/BluetoothDeviceFilter.java +++ b/core/java/android/companion/BluetoothDeviceFilter.java @@ -31,6 +31,7 @@ import android.os.ParcelUuid; import android.provider.OneTimeUseBuilder; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import java.util.ArrayList; import java.util.List; @@ -53,8 +54,8 @@ public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice List<ParcelUuid> serviceUuidMasks) { mNamePattern = namePattern; mAddress = address; - mServiceUuids = ArrayUtils.emptyIfNull(serviceUuids); - mServiceUuidMasks = ArrayUtils.emptyIfNull(serviceUuidMasks); + mServiceUuids = CollectionUtils.emptyIfNull(serviceUuids); + mServiceUuidMasks = CollectionUtils.emptyIfNull(serviceUuidMasks); } private BluetoothDeviceFilter(Parcel in) { diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 7906707c0133..15bd175949c4 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -26,6 +26,7 @@ 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 @@ -247,6 +248,36 @@ public class Binder implements IBinder { public static final native void restoreCallingIdentity(long token); /** + * Convenience method for running the provided action enclosed in + * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} + * + * @hide + */ + public static final void withCleanCallingIdentity(Runnable action) { + long callingIdentity = clearCallingIdentity(); + try { + action.run(); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + + /** + * Convenience method for running the provided action enclosed in + * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result + * + * @hide + */ + public static final <T> T withCleanCallingIdentity(Supplier<T> action) { + long callingIdentity = clearCallingIdentity(); + try { + return action.get(); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + + /** * Sets the native thread-local StrictMode policy mask. * * <p>The StrictMode settings are kept in two places: a Java-level diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java index c43251932eda..a4d5c6f4b202 100644 --- a/core/java/android/os/HandlerThread.java +++ b/core/java/android/os/HandlerThread.java @@ -16,6 +16,9 @@ package android.os; +import android.annotation.NonNull; +import android.annotation.Nullable; + /** * Handy class for starting a new thread that has a looper. The looper can then be * used to create handler classes. Note that start() must still be called. @@ -24,6 +27,7 @@ public class HandlerThread extends Thread { int mPriority; int mTid = -1; Looper mLooper; + private @Nullable Handler mHandler; public HandlerThread(String name) { super(name); @@ -86,6 +90,18 @@ public class HandlerThread extends Thread { } /** + * @return a shared {@link Handler} associated with this thread + * @hide + */ + @NonNull + public Handler getThreadHandler() { + if (mHandler == null) { + mHandler = new Handler(getLooper()); + } + return mHandler; + } + + /** * Quits the handler thread's looper. * <p> * Causes the handler thread's looper to terminate without processing any diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index d0fbe7c8a666..f4dd5a62112c 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -31,7 +31,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.function.Function; /** * ArrayUtils contains some methods that you can call to find out @@ -237,35 +236,6 @@ public class ArrayUtils { return false; } - @NonNull - public static <T> List<T> filter(@Nullable List<?> list, Class<T> c) { - if (isEmpty(list)) return Collections.emptyList(); - ArrayList<T> result = null; - for (int i = 0; i < list.size(); i++) { - final Object item = list.get(i); - if (c.isInstance(item)) { - result = add(result, (T) item); - } - } - return emptyIfNull(result); - } - - public static <T> boolean any(@Nullable List<T> items, - java.util.function.Predicate<T> predicate) { - return find(items, predicate) != null; - } - - @Nullable - public static <T> T find(@Nullable List<T> items, - java.util.function.Predicate<T> predicate) { - 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; - } - return null; - } - public static long total(@Nullable long[] array) { long total = 0; if (array != null) { @@ -504,29 +474,6 @@ public class ArrayUtils { } } - public static int size(@Nullable Collection<?> cur) { - return cur != null ? cur.size() : 0; - } - - 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(); - final ArrayList<O> result = new ArrayList<>(); - for (int i = 0; i < cur.size(); i++) { - result.add(f.apply(cur.get(i))); - } - return result; - } - - /** - * Returns the given list, or an immutable empty list if the provided list is null - * - * @see Collections#emptyList - */ - public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) { - return cur == null ? Collections.emptyList() : cur; - } - public static <T> boolean contains(@Nullable Collection<T> cur, T val) { return (cur != null) ? cur.contains(val) : false; } diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java new file mode 100644 index 000000000000..287f68cf5a55 --- /dev/null +++ b/core/java/com/android/internal/util/CollectionUtils.java @@ -0,0 +1,130 @@ +/* + * 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 android.annotation.NonNull; +import android.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Utility methods for dealing with (typically {@link Nullable}) {@link Collection}s + * + * Unless a method specifies otherwise, a null value for a collection is treated as an empty + * collection of that type. + */ +public class CollectionUtils { + private CollectionUtils() { /* cannot be instantiated */ } + + /** + * Returns a list of items from the provided list that match the given condition. + * + * This is similar to {@link Stream#filter} but without the overhead of creating an intermediate + * {@link Stream} instance + */ + public static @NonNull <T> List<T> filter(@Nullable List<T> list, + java.util.function.Predicate<? super T> predicate) { + ArrayList<T> result = null; + for (int i = 0; i < size(list); i++) { + final T item = list.get(i); + if (predicate.test(item)) { + result = ArrayUtils.add(result, item); + } + } + return emptyIfNull(result); + } + + /** + * Returns a list of items resulting from applying the given function to each element of the + * provided list. + * + * The resulting list will have the same {@link #size} as the input one. + * + * This is similar to {@link Stream#map} but without the overhead of creating an intermediate + * {@link Stream} instance + */ + 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(); + final ArrayList<O> result = new ArrayList<>(); + for (int i = 0; i < cur.size(); i++) { + result.add(f.apply(cur.get(i))); + } + 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 + * + * @see Collections#emptyList + */ + public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) { + return cur == null ? Collections.emptyList() : cur; + } + + /** + * Returns the size of the given list, or 0 if the list is null + */ + public static int size(@Nullable Collection<?> cur) { + return cur != null ? cur.size() : 0; + } + + /** + * 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(); + ArrayList<T> result = null; + for (int i = 0; i < list.size(); i++) { + final Object item = list.get(i); + if (c.isInstance(item)) { + result = ArrayUtils.add(result, (T) item); + } + } + return emptyIfNull(result); + } + + /** + * Returns whether there exists at least one element in the list for which + * condition {@code predicate} is true + */ + public static <T> boolean any(@Nullable List<T> items, + java.util.function.Predicate<T> predicate) { + return find(items, predicate) != null; + } + + /** + * Returns the first element from the list for which + * condition {@code predicate} is true, or null if there is no such element + */ + public static @Nullable <T> T find(@Nullable List<T> items, + java.util.function.Predicate<T> predicate) { + if (ArrayUtils.isEmpty(items)) return null; + for (int i = 0; i < items.size(); i++) { + final T item = items.get(i); + if (predicate.test(item)) return item; + } + return null; + } +} diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index e1e60bb99374..e49463f04ec6 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -35,9 +35,7 @@ import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.companion.AssociationRequest; import android.companion.BluetoothDeviceFilter; -import android.companion.BluetoothDeviceFilterUtils; import android.companion.BluetoothLEDeviceFilter; -import android.companion.CompanionDeviceManager; import android.companion.DeviceFilter; import android.companion.ICompanionDeviceDiscoveryService; import android.companion.ICompanionDeviceDiscoveryServiceCallback; @@ -60,7 +58,7 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; -import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.Preconditions; import java.util.ArrayList; @@ -185,10 +183,10 @@ public class DeviceDiscoveryService extends Service { mRequest = request; mFilters = request.getDeviceFilters(); - mWifiFilters = ArrayUtils.filter(mFilters, WifiDeviceFilter.class); - mBluetoothFilters = ArrayUtils.filter(mFilters, BluetoothDeviceFilter.class); - mBLEFilters = ArrayUtils.filter(mFilters, BluetoothLEDeviceFilter.class); - mBLEScanFilters = ArrayUtils.map(mBLEFilters, BluetoothLEDeviceFilter::getScanFilter); + mWifiFilters = CollectionUtils.filter(mFilters, WifiDeviceFilter.class); + mBluetoothFilters = CollectionUtils.filter(mFilters, BluetoothDeviceFilter.class); + mBLEFilters = CollectionUtils.filter(mFilters, BluetoothLEDeviceFilter.class); + mBLEScanFilters = CollectionUtils.map(mBLEFilters, BluetoothLEDeviceFilter::getScanFilter); reset(); @@ -357,7 +355,7 @@ public class DeviceDiscoveryService extends Service { public static <T extends Parcelable> DeviceFilterPair<T> findMatch( T dev, @Nullable List<? extends DeviceFilter<T>> filters) { if (isEmpty(filters)) return new DeviceFilterPair<>(dev, null); - final DeviceFilter<T> matchingFilter = ArrayUtils.find(filters, (f) -> f.matches(dev)); + final DeviceFilter<T> matchingFilter = CollectionUtils.find(filters, (f) -> f.matches(dev)); return matchingFilter != null ? new DeviceFilterPair<>(dev, matchingFilter) : null; } diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 44ca6a916c47..3e2dae55988c 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -1136,6 +1136,9 @@ public class DeviceIdleController extends SystemService private final class BinderService extends IDeviceIdleController.Stub { @Override public void addPowerSaveWhitelistApp(String name) { + if (DEBUG) { + Slog.i(TAG, "addPowerSaveWhitelistApp(name = " + name + ")"); + } getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); long ident = Binder.clearCallingIdentity(); @@ -1147,6 +1150,9 @@ public class DeviceIdleController extends SystemService } @Override public void removePowerSaveWhitelistApp(String name) { + if (DEBUG) { + Slog.i(TAG, "removePowerSaveWhitelistApp(name = " + name + ")"); + } getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); long ident = Binder.clearCallingIdentity(); diff --git a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java index e6e2cb3d99c9..9356dacc29f8 100644 --- a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java +++ b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java @@ -46,7 +46,10 @@ import android.util.ExceptionUtils; import android.util.Slog; import android.util.Xml; +import com.android.internal.content.PackageMonitor; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; +import com.android.server.FgThread; import com.android.server.SystemService; import org.xmlpull.v1.XmlPullParser; @@ -86,10 +89,35 @@ public class CompanionDeviceManagerService extends SystemService { private final CompanionDeviceManagerImpl mImpl; private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>(); + private IDeviceIdleController mIdleController; public CompanionDeviceManagerService(Context context) { super(context); mImpl = new CompanionDeviceManagerImpl(); + mIdleController = IDeviceIdleController.Stub.asInterface( + ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); + registerPackageMonitor(); + } + + private void registerPackageMonitor() { + new PackageMonitor() { + @Override + public void onPackageRemoved(String packageName, int uid) { + updateAssociations( + as -> CollectionUtils.filter(as, + a -> !Objects.equals(a.companionAppPackage, packageName)), + getChangingUserId()); + } + + @Override + public void onPackageModified(String packageName) { + int userId = getChangingUserId(); + if (!ArrayUtils.isEmpty(readAllAssociations(userId, packageName))) { + updateSpecialAccessPermissionForAssociatedPackage(packageName, userId); + } + } + + }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true); } @Override @@ -124,9 +152,9 @@ public class CompanionDeviceManagerService extends SystemService { @Override public List<String> getAssociations(String callingPackage) { - return ArrayUtils.map( + return CollectionUtils.map( readAllAssociations(getUserId(), callingPackage), - (a) -> a.deviceAddress); + a -> a.deviceAddress); } @Override @@ -178,43 +206,55 @@ public class CompanionDeviceManagerService extends SystemService { @Override public void onDeviceSelected(String packageName, int userId, String deviceAddress) { //TODO unbind - grantSpecialAccessPermissionsIfNeeded(packageName, userId); + updateSpecialAccessPermissionForAssociatedPackage(packageName, userId); recordAssociation(packageName, deviceAddress); } }; } - private void grantSpecialAccessPermissionsIfNeeded(String packageName, int userId) { - final long identity = Binder.clearCallingIdentity(); - final PackageInfo packageInfo; - try { + private void updateSpecialAccessPermissionForAssociatedPackage(String packageName, int userId) { + PackageInfo packageInfo = getPackageInfo(packageName, userId); + if (packageInfo == null) { + return; + } + + Binder.withCleanCallingIdentity(() -> { try { - packageInfo = getContext().getPackageManager().getPackageInfoAsUser( - packageName, PackageManager.GET_PERMISSIONS, userId); - } catch (PackageManager.NameNotFoundException e) { - Slog.e(LOG_TAG, "Error granting special access permissions to package:" - + packageName, e); - return; - } - if (ArrayUtils.contains(packageInfo.requestedPermissions, - Manifest.permission.RUN_IN_BACKGROUND)) { - IDeviceIdleController idleController = IDeviceIdleController.Stub.asInterface( - ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); - try { - idleController.addPowerSaveWhitelistApp(packageName); - } catch (RemoteException e) { - /* ignore - local call */ + if (ArrayUtils.contains(packageInfo.requestedPermissions, + Manifest.permission.RUN_IN_BACKGROUND)) { + mIdleController.addPowerSaveWhitelistApp(packageInfo.packageName); + } else { + mIdleController.removePowerSaveWhitelistApp(packageInfo.packageName); } + } catch (RemoteException e) { + /* ignore - local call */ } + + NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext()); if (ArrayUtils.contains(packageInfo.requestedPermissions, Manifest.permission.USE_DATA_IN_BACKGROUND)) { - NetworkPolicyManager.from(getContext()).addUidPolicy( + networkPolicyManager.addUidPolicy( + packageInfo.applicationInfo.uid, + NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND); + } else { + networkPolicyManager.removeUidPolicy( packageInfo.applicationInfo.uid, NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND); } - } finally { - Binder.restoreCallingIdentity(identity); - } + }); + } + + @Nullable + private PackageInfo getPackageInfo(String packageName, int userId) { + return Binder.withCleanCallingIdentity(() -> { + try { + return getContext().getPackageManager().getPackageInfoAsUser( + packageName, PackageManager.GET_PERMISSIONS, userId); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + packageName, e); + return null; + } + }); } private void recordAssociation(String priviledgedPackage, String deviceAddress) { @@ -222,13 +262,16 @@ public class CompanionDeviceManagerService extends SystemService { new Association(getUserId(), deviceAddress, priviledgedPackage))); } - private void updateAssociations( - Function<ArrayList<Association>, ArrayList<Association>> update) { - final int userId = getUserId(); + private void updateAssociations(Function<ArrayList<Association>, List<Association>> update) { + updateAssociations(update, getUserId()); + } + + private void updateAssociations(Function<ArrayList<Association>, List<Association>> update, + int userId) { final AtomicFile file = getStorageFileForUser(userId); synchronized (file) { final ArrayList<Association> old = readAllAssociations(userId); - final ArrayList<Association> associations = update.apply(old); + final List<Association> associations = update.apply(old); if (Objects.equals(old, associations)) return; file.write((out) -> { @@ -239,7 +282,7 @@ public class CompanionDeviceManagerService extends SystemService { xml.startDocument(null, true); xml.startTag(null, XML_TAG_ASSOCIATIONS); - for (int i = 0; i < ArrayUtils.size(associations); i++) { + for (int i = 0; i < CollectionUtils.size(associations); i++) { Association association = associations.get(i); xml.startTag(null, XML_TAG_ASSOCIATION) .attribute(null, XML_ATTR_PACKAGE, association.companionAppPackage) |