summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/SystemServiceRegistry.java1
-rw-r--r--core/java/android/companion/AssociationRequest.java15
-rw-r--r--core/java/android/companion/BluetoothDeviceFilter.java17
-rw-r--r--core/java/android/companion/BluetoothLEDeviceFilter.java64
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java12
-rw-r--r--core/java/android/companion/WifiDeviceFilter.java14
-rw-r--r--core/java/android/os/Handler.java18
-rw-r--r--core/java/android/util/ExceptionUtils.java21
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java36
-rw-r--r--services/java/com/android/server/SystemServer.java2
-rw-r--r--services/print/java/com/android/server/print/CompanionDeviceManagerService.java77
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() {