diff options
11 files changed, 239 insertions, 38 deletions
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 124749a43d61..f719749cc9f9 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -93,6 +93,7 @@ import android.nfc.NfcManager; import android.os.BatteryManager; import android.os.BatteryStats; import android.os.Build; +import android.os.Debug; import android.os.DropBoxManager; import android.os.HardwarePropertiesManager; import android.os.IBatteryPropertiesRegistrar; diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index bb844a327168..919f4baf3b86 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -27,6 +27,7 @@ import com.android.internal.util.CollectionUtils; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * A request for the user to select a companion device to associate with. @@ -69,6 +70,20 @@ public final class AssociationRequest implements Parcelable { } @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AssociationRequest that = (AssociationRequest) o; + return mSingleDevice == that.mSingleDevice && + Objects.equals(mDeviceFilters, that.mDeviceFilters); + } + + @Override + public int hashCode() { + return Objects.hash(mSingleDevice, mDeviceFilters); + } + + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeByte((byte) (mSingleDevice ? 1 : 0)); dest.writeParcelableList(mDeviceFilters, flags); diff --git a/core/java/android/companion/BluetoothDeviceFilter.java b/core/java/android/companion/BluetoothDeviceFilter.java index 1d8df7f26f6e..84e15364c191 100644 --- a/core/java/android/companion/BluetoothDeviceFilter.java +++ b/core/java/android/companion/BluetoothDeviceFilter.java @@ -35,6 +35,7 @@ import com.android.internal.util.CollectionUtils; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.regex.Pattern; /** @@ -123,6 +124,22 @@ public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice } @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BluetoothDeviceFilter that = (BluetoothDeviceFilter) o; + return Objects.equals(mNamePattern, that.mNamePattern) && + Objects.equals(mAddress, that.mAddress) && + Objects.equals(mServiceUuids, that.mServiceUuids) && + Objects.equals(mServiceUuidMasks, that.mServiceUuidMasks); + } + + @Override + public int hashCode() { + return Objects.hash(mNamePattern, mAddress, mServiceUuids, mServiceUuidMasks); + } + + @Override public int describeContents() { return 0; } diff --git a/core/java/android/companion/BluetoothLEDeviceFilter.java b/core/java/android/companion/BluetoothLEDeviceFilter.java index e057fbcc901a..0444775871b9 100644 --- a/core/java/android/companion/BluetoothLEDeviceFilter.java +++ b/core/java/android/companion/BluetoothLEDeviceFilter.java @@ -36,6 +36,8 @@ import com.android.internal.util.BitUtils; import com.android.internal.util.ObjectUtils; import com.android.internal.util.Preconditions; +import java.util.Arrays; +import java.util.Objects; import java.util.regex.Pattern; /** @@ -160,9 +162,39 @@ public final class BluetoothLEDeviceFilter implements DeviceFilter<ScanResult> { } @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BluetoothLEDeviceFilter that = (BluetoothLEDeviceFilter) o; + return mRenameBytesFrom == that.mRenameBytesFrom && + mRenameBytesTo == that.mRenameBytesTo && + mRenameBytesReverseOrder == that.mRenameBytesReverseOrder && + Objects.equals(mNamePattern, that.mNamePattern) && + Objects.equals(mScanFilter, that.mScanFilter) && + Arrays.equals(mRawDataFilter, that.mRawDataFilter) && + Arrays.equals(mRawDataFilterMask, that.mRawDataFilterMask) && + Objects.equals(mRenamePrefix, that.mRenamePrefix) && + Objects.equals(mRenameSuffix, that.mRenameSuffix); + } + + @Override + public int hashCode() { + return Objects.hash(mNamePattern, mScanFilter, mRawDataFilter, mRawDataFilterMask, + mRenamePrefix, mRenameSuffix, mRenameBytesFrom, mRenameBytesTo, + mRenameBytesReverseOrder); + } + + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(patternToString(getNamePattern())); dest.writeParcelable(mScanFilter, flags); + dest.writeByteArray(mRawDataFilter); + dest.writeByteArray(mRawDataFilterMask); + dest.writeString(mRenamePrefix); + dest.writeString(mRenameSuffix); + dest.writeInt(mRenameBytesFrom); + dest.writeInt(mRenameBytesTo); + dest.writeBoolean(mRenameBytesReverseOrder); } @Override @@ -174,13 +206,23 @@ public final class BluetoothLEDeviceFilter implements DeviceFilter<ScanResult> { = new Creator<BluetoothLEDeviceFilter>() { @Override public BluetoothLEDeviceFilter createFromParcel(Parcel in) { - return new BluetoothLEDeviceFilter.Builder() + Builder builder = new Builder() .setNamePattern(patternFromString(in.readString())) - .setScanFilter(in.readParcelable(null)) - .setRawDataFilter(in.readBlob(), in.readBlob()) - .setRename(in.readString(), in.readString(), - in.readInt(), in.readInt(), in.readBoolean()) - .build(); + .setScanFilter(in.readParcelable(null)); + byte[] rawDataFilter = in.createByteArray(); + byte[] rawDataFilterMask = in.createByteArray(); + if (rawDataFilter != null) { + builder.setRawDataFilter(rawDataFilter, rawDataFilterMask); + } + String renamePrefix = in.readString(); + String suffix = in.readString(); + int bytesFrom = in.readInt(); + int bytesTo = in.readInt(); + boolean bytesReverseOrder = in.readBoolean(); + if (renamePrefix != null) { + builder.setRename(renamePrefix, suffix, bytesFrom, bytesTo, bytesReverseOrder); + } + return builder.build(); } @Override @@ -240,12 +282,14 @@ public final class BluetoothLEDeviceFilter implements DeviceFilter<ScanResult> { */ @NonNull public Builder setRawDataFilter(@NonNull byte[] rawDataFilter, - @NonNull byte[] rawDataFilterMask) { + @Nullable byte[] rawDataFilterMask) { checkNotUsed(); - checkArgument(rawDataFilter.length == rawDataFilterMask.length, + Preconditions.checkNotNull(rawDataFilter); + checkArgument(rawDataFilterMask == null || + rawDataFilter.length == rawDataFilterMask.length, "Mask and filter should be the same length"); - mRawDataFilter = Preconditions.checkNotNull(rawDataFilter); - mRawDataFilterMask = Preconditions.checkNotNull(rawDataFilterMask); + mRawDataFilter = rawDataFilter; + mRawDataFilterMask = rawDataFilterMask; return this; } diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index ecdc0ce902ef..0d8e36501cbc 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -17,6 +17,8 @@ package android.companion; +import static com.android.internal.util.Preconditions.checkNotNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; @@ -24,7 +26,6 @@ import android.content.Context; import android.content.IntentSender; import android.content.pm.PackageManager; import android.os.Handler; -import android.os.Looper; import android.os.RemoteException; import android.util.Log; @@ -43,7 +44,7 @@ import java.util.List; */ public final class CompanionDeviceManager { - private static final boolean DEBUG = false; //TODO + private static final boolean DEBUG = false; private static final String LOG_TAG = "CompanionDeviceManager"; /** @@ -129,10 +130,9 @@ public final class CompanionDeviceManager { if (!checkFeaturePresent()) { return; } - - final Handler finalHandler = handler != null - ? handler - : new Handler(Looper.getMainLooper()); + checkNotNull(request, "Request cannot be null"); + checkNotNull(callback, "Callback cannot be null"); + final Handler finalHandler = Handler.mainIfNull(handler); try { mService.associate( request, diff --git a/core/java/android/companion/WifiDeviceFilter.java b/core/java/android/companion/WifiDeviceFilter.java index 1ab9ce11cb0f..b6e704c39998 100644 --- a/core/java/android/companion/WifiDeviceFilter.java +++ b/core/java/android/companion/WifiDeviceFilter.java @@ -29,6 +29,7 @@ import android.net.wifi.ScanResult; import android.os.Parcel; import android.provider.OneTimeUseBuilder; +import java.util.Objects; import java.util.regex.Pattern; /** @@ -75,6 +76,19 @@ public final class WifiDeviceFilter implements DeviceFilter<ScanResult> { } @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WifiDeviceFilter that = (WifiDeviceFilter) o; + return Objects.equals(mNamePattern, that.mNamePattern); + } + + @Override + public int hashCode() { + return Objects.hash(mNamePattern); + } + + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(patternToString(getNamePattern())); } diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index 3c7c962eaa12..8678d95db17d 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -16,6 +16,8 @@ package android.os; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.util.Log; import android.util.Printer; @@ -69,6 +71,7 @@ public class Handler { */ private static final boolean FIND_POTENTIAL_LEAKS = false; private static final String TAG = "Handler"; + private static Handler MAIN_THREAD_HANDLER = null; /** * Callback interface you can use when instantiating a Handler to avoid @@ -231,6 +234,21 @@ public class Handler { mAsynchronous = async; } + /** @hide */ + @NonNull + public static Handler getMain() { + if (MAIN_THREAD_HANDLER == null) { + MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper()); + } + return MAIN_THREAD_HANDLER; + } + + /** @hide */ + @NonNull + public static Handler mainIfNull(@Nullable Handler handler) { + return handler == null ? getMain() : handler; + } + /** {@hide} */ public String getTraceName(Message message) { final StringBuilder sb = new StringBuilder(); diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java index 87231e106ca3..44019c32560d 100644 --- a/core/java/android/util/ExceptionUtils.java +++ b/core/java/android/util/ExceptionUtils.java @@ -17,6 +17,7 @@ package android.util; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.ParcelableException; import com.android.internal.util.Preconditions; @@ -55,10 +56,26 @@ public class ExceptionUtils { return getCompleteMessage(null, t); } + public static <E extends Throwable> void propagateIfInstanceOf( + @Nullable Throwable t, Class<E> c) throws E { + if (t != null && c.isInstance(t)) { + throw c.cast(t); + } + } + + /** + * @param <E> a checked exception that is ok to throw without wrapping + */ + public static <E extends Exception> RuntimeException propagate(@NonNull Throwable t, Class<E> c) + throws E { + propagateIfInstanceOf(t, c); + return propagate(t); + } + public static RuntimeException propagate(@NonNull Throwable t) { Preconditions.checkNotNull(t); - if (t instanceof Error) throw (Error)t; - if (t instanceof RuntimeException) throw (RuntimeException)t; + propagateIfInstanceOf(t, Error.class); + propagateIfInstanceOf(t, RuntimeException.class); throw new RuntimeException(t); } } diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index e49463f04ec6..1b6aca1ba65e 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -58,6 +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; @@ -180,15 +181,20 @@ public class DeviceDiscoveryService extends Service { } private void startDiscovery(AssociationRequest request) { - mRequest = request; + if (!request.equals(mRequest)) { + mRequest = request; - mFilters = request.getDeviceFilters(); - mWifiFilters = CollectionUtils.filter(mFilters, WifiDeviceFilter.class); - mBluetoothFilters = CollectionUtils.filter(mFilters, BluetoothDeviceFilter.class); - mBLEFilters = CollectionUtils.filter(mFilters, BluetoothLEDeviceFilter.class); - mBLEScanFilters = CollectionUtils.map(mBLEFilters, BluetoothLEDeviceFilter::getScanFilter); + mFilters = request.getDeviceFilters(); + 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(); + reset(); + } + if (!ArrayUtils.isEmpty(mDevicesFound)) { + onReadyToShowUI(); + } if (shouldScan(mBluetoothFilters)) { final IntentFilter intentFilter = new IntentFilter(); @@ -228,10 +234,18 @@ public class DeviceDiscoveryService extends Service { private void stopScan() { if (DEBUG) Log.i(LOG_TAG, "stopScan() called"); - mBluetoothAdapter.cancelDiscovery(); - mBLEScanner.stopScan(mBLEScanCallback); - unregisterReceiver(mBluetoothDeviceFoundBroadcastReceiver); - unregisterReceiver(mWifiDeviceFoundBroadcastReceiver); + + if (shouldScan(mBluetoothFilters)) { + mBluetoothAdapter.cancelDiscovery(); + unregisterReceiver(mBluetoothDeviceFoundBroadcastReceiver); + } + if (shouldScan(mBLEFilters)) { + mBLEScanner.stopScan(mBLEScanCallback); + } + if (shouldScan(mWifiFilters)) { + unregisterReceiver(mWifiDeviceFoundBroadcastReceiver); + } + stopSelf(); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 9ed7c93b52df..2165fe566aee 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -307,7 +307,7 @@ public final class SystemServer { // In case the runtime switched since last boot (such as when // the old runtime was removed in an OTA), set the system - // property so that it is in sync. We can't do this in + // property so that it is in sync. We can | xq oqi't do this in // libnativehelper's JniInvocation::Init code where we already // had to fallback to a different runtime because it is // running as root and we need to be the system user to set diff --git a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java index 9356dacc29f8..7790698d02e9 100644 --- a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java +++ b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java @@ -20,6 +20,7 @@ package com.android.server.print; import static com.android.internal.util.Preconditions.checkNotNull; import android.Manifest; +import android.annotation.CheckResult; import android.annotation.Nullable; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; @@ -36,8 +37,11 @@ import android.content.pm.PackageManager; import android.net.NetworkPolicyManager; import android.os.Binder; import android.os.Environment; +import android.os.Handler; import android.os.IBinder; import android.os.IDeviceIdleController; +import android.os.IInterface; +import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -68,11 +72,13 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Function; //TODO move to own package! -//TODO un/linkToDeath & onBinderDied - unbind //TODO onStop schedule unbind in 5 seconds -//TODO Prune association on app uninstall +//TODO make sure APIs are only callable from currently focused app +//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) /** @hide */ -public class CompanionDeviceManagerService extends SystemService { +public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient { private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative( CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, @@ -90,6 +96,8 @@ public class CompanionDeviceManagerService extends SystemService { private final CompanionDeviceManagerImpl mImpl; private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>(); private IDeviceIdleController mIdleController; + private IFindDeviceCallback mFindDeviceCallback; + private ServiceConnection mServiceConnection; public CompanionDeviceManagerService(Context context) { super(context); @@ -125,7 +133,51 @@ public class CompanionDeviceManagerService extends SystemService { publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl); } + @Override + public void binderDied() { + Handler.getMain().post(this::handleBinderDied); + } + + private void handleBinderDied() { + mServiceConnection = unbind(mServiceConnection); + mFindDeviceCallback = unlinkToDeath(mFindDeviceCallback, this, 0); + } + + /** + * Usage: {@code a = unlinkToDeath(a, deathRecipient, flags); } + */ + @Nullable + @CheckResult + private static <T extends IInterface> T unlinkToDeath(T iinterface, + IBinder.DeathRecipient deathRecipient, int flags) { + if (iinterface != null) { + iinterface.asBinder().unlinkToDeath(deathRecipient, flags); + } + return null; + } + + @Nullable + @CheckResult + private ServiceConnection unbind(@Nullable ServiceConnection conn) { + if (conn != null) { + getContext().unbindService(conn); + } + return null; + } + class CompanionDeviceManagerImpl extends ICompanionDeviceManager.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 associate( AssociationRequest request, @@ -135,14 +187,14 @@ public class CompanionDeviceManagerService extends SystemService { Slog.i(LOG_TAG, "associate(request = " + request + ", callback = " + callback + ", callingPackage = " + callingPackage + ")"); } - checkNotNull(request); - checkNotNull(callback); + checkNotNull(request, "Request cannot be null"); + checkNotNull(callback, "Callback cannot be null"); final long callingIdentity = Binder.clearCallingIdentity(); try { //TODO bindServiceAsUser getContext().bindService( new Intent().setComponent(SERVICE_TO_BIND_TO), - getServiceConnection(request, callback, callingPackage), + createServiceConnection(request, callback, callingPackage), Context.BIND_AUTO_CREATE); } finally { Binder.restoreCallingIdentity(callingIdentity); @@ -168,11 +220,11 @@ public class CompanionDeviceManagerService extends SystemService { return UserHandle.getUserId(Binder.getCallingUid()); } - private ServiceConnection getServiceConnection( + private ServiceConnection createServiceConnection( final AssociationRequest request, final IFindDeviceCallback findDeviceCallback, final String callingPackage) { - return new ServiceConnection() { + mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG) { @@ -180,6 +232,14 @@ public class CompanionDeviceManagerService extends SystemService { "onServiceConnected(name = " + name + ", service = " + service + ")"); } + mFindDeviceCallback = findDeviceCallback; + try { + mFindDeviceCallback.asBinder().linkToDeath( + CompanionDeviceManagerService.this, 0); + } catch (RemoteException e) { + handleBinderDied(); + return; + } try { ICompanionDeviceDiscoveryService.Stub .asInterface(service) @@ -198,6 +258,7 @@ public class CompanionDeviceManagerService extends SystemService { if (DEBUG) Slog.i(LOG_TAG, "onServiceDisconnected(name = " + name + ")"); } }; + return mServiceConnection; } private ICompanionDeviceDiscoveryServiceCallback.Stub getServiceCallback() { |