Merge "[DO NOT MERGE] Scan and notify apps when their companion devices are nearby" into sc-dev
This commit is contained in:
@@ -3197,6 +3197,61 @@ public final class BluetoothAdapter {
|
|||||||
void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
|
void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a callback to receive events whenever the bluetooth stack goes down and back up,
|
||||||
|
* e.g. in the event the bluetooth is turned off/on via settings.
|
||||||
|
*
|
||||||
|
* If the bluetooth stack is currently up, there will not be an initial callback call.
|
||||||
|
* You can use the return value as an indication of this being the case.
|
||||||
|
*
|
||||||
|
* Callbacks will be delivered on a binder thread.
|
||||||
|
*
|
||||||
|
* @return whether bluetooth is already up currently
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) {
|
||||||
|
return getBluetoothService(callback.mRemote) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a callback registered via {@link #registerServiceLifecycleCallback}
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) {
|
||||||
|
removeServiceStateCallback(callback.mRemote);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback for {@link #registerServiceLifecycleCallback}
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public abstract static class ServiceLifecycleCallback {
|
||||||
|
|
||||||
|
/** Called when the bluetooth stack is up */
|
||||||
|
public abstract void onBluetoothServiceUp();
|
||||||
|
|
||||||
|
/** Called when the bluetooth stack is down */
|
||||||
|
public abstract void onBluetoothServiceDown();
|
||||||
|
|
||||||
|
IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() {
|
||||||
|
@Override
|
||||||
|
public void onBluetoothServiceUp(IBluetooth bluetoothService) {
|
||||||
|
ServiceLifecycleCallback.this.onBluetoothServiceUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBluetoothServiceDown() {
|
||||||
|
ServiceLifecycleCallback.this.onBluetoothServiceDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBrEdrDown() {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a scan for Bluetooth LE devices.
|
* Starts a scan for Bluetooth LE devices.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public final class Association implements Parcelable {
|
|||||||
private final @NonNull String mDeviceMacAddress;
|
private final @NonNull String mDeviceMacAddress;
|
||||||
private final @NonNull String mPackageName;
|
private final @NonNull String mPackageName;
|
||||||
private final @Nullable String mDeviceProfile;
|
private final @Nullable String mDeviceProfile;
|
||||||
private final boolean mKeepProfilePrivilegesWhenDeviceAway;
|
private final boolean mNotifyOnDeviceNearby;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public int getUserId() {
|
public int getUserId() {
|
||||||
@@ -47,7 +47,7 @@ public final class Association implements Parcelable {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Code below generated by codegen v1.0.21.
|
// Code below generated by codegen v1.0.22.
|
||||||
//
|
//
|
||||||
// DO NOT MODIFY!
|
// DO NOT MODIFY!
|
||||||
// CHECKSTYLE:OFF Generated code
|
// CHECKSTYLE:OFF Generated code
|
||||||
@@ -71,7 +71,7 @@ public final class Association implements Parcelable {
|
|||||||
@NonNull String deviceMacAddress,
|
@NonNull String deviceMacAddress,
|
||||||
@NonNull String packageName,
|
@NonNull String packageName,
|
||||||
@Nullable String deviceProfile,
|
@Nullable String deviceProfile,
|
||||||
boolean keepProfilePrivilegesWhenDeviceAway) {
|
boolean notifyOnDeviceNearby) {
|
||||||
this.mUserId = userId;
|
this.mUserId = userId;
|
||||||
com.android.internal.util.AnnotationValidations.validate(
|
com.android.internal.util.AnnotationValidations.validate(
|
||||||
UserIdInt.class, null, mUserId);
|
UserIdInt.class, null, mUserId);
|
||||||
@@ -82,7 +82,7 @@ public final class Association implements Parcelable {
|
|||||||
com.android.internal.util.AnnotationValidations.validate(
|
com.android.internal.util.AnnotationValidations.validate(
|
||||||
NonNull.class, null, mPackageName);
|
NonNull.class, null, mPackageName);
|
||||||
this.mDeviceProfile = deviceProfile;
|
this.mDeviceProfile = deviceProfile;
|
||||||
this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
|
this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
|
||||||
|
|
||||||
// onConstructed(); // You can define this method to get a callback
|
// onConstructed(); // You can define this method to get a callback
|
||||||
}
|
}
|
||||||
@@ -103,8 +103,8 @@ public final class Association implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DataClass.Generated.Member
|
@DataClass.Generated.Member
|
||||||
public boolean isKeepProfilePrivilegesWhenDeviceAway() {
|
public boolean isNotifyOnDeviceNearby() {
|
||||||
return mKeepProfilePrivilegesWhenDeviceAway;
|
return mNotifyOnDeviceNearby;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -118,7 +118,7 @@ public final class Association implements Parcelable {
|
|||||||
"deviceMacAddress = " + mDeviceMacAddress + ", " +
|
"deviceMacAddress = " + mDeviceMacAddress + ", " +
|
||||||
"packageName = " + mPackageName + ", " +
|
"packageName = " + mPackageName + ", " +
|
||||||
"deviceProfile = " + mDeviceProfile + ", " +
|
"deviceProfile = " + mDeviceProfile + ", " +
|
||||||
"keepProfilePrivilegesWhenDeviceAway = " + mKeepProfilePrivilegesWhenDeviceAway +
|
"notifyOnDeviceNearby = " + mNotifyOnDeviceNearby +
|
||||||
" }";
|
" }";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ public final class Association implements Parcelable {
|
|||||||
&& Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
|
&& Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
|
||||||
&& Objects.equals(mPackageName, that.mPackageName)
|
&& Objects.equals(mPackageName, that.mPackageName)
|
||||||
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
|
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
|
||||||
&& mKeepProfilePrivilegesWhenDeviceAway == that.mKeepProfilePrivilegesWhenDeviceAway;
|
&& mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -153,7 +153,7 @@ public final class Association implements Parcelable {
|
|||||||
_hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress);
|
_hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress);
|
||||||
_hash = 31 * _hash + Objects.hashCode(mPackageName);
|
_hash = 31 * _hash + Objects.hashCode(mPackageName);
|
||||||
_hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
|
_hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
|
||||||
_hash = 31 * _hash + Boolean.hashCode(mKeepProfilePrivilegesWhenDeviceAway);
|
_hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby);
|
||||||
return _hash;
|
return _hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ public final class Association implements Parcelable {
|
|||||||
// void parcelFieldName(Parcel dest, int flags) { ... }
|
// void parcelFieldName(Parcel dest, int flags) { ... }
|
||||||
|
|
||||||
byte flg = 0;
|
byte flg = 0;
|
||||||
if (mKeepProfilePrivilegesWhenDeviceAway) flg |= 0x10;
|
if (mNotifyOnDeviceNearby) flg |= 0x10;
|
||||||
if (mDeviceProfile != null) flg |= 0x8;
|
if (mDeviceProfile != null) flg |= 0x8;
|
||||||
dest.writeByte(flg);
|
dest.writeByte(flg);
|
||||||
dest.writeInt(mUserId);
|
dest.writeInt(mUserId);
|
||||||
@@ -185,7 +185,7 @@ public final class Association implements Parcelable {
|
|||||||
// static FieldType unparcelFieldName(Parcel in) { ... }
|
// static FieldType unparcelFieldName(Parcel in) { ... }
|
||||||
|
|
||||||
byte flg = in.readByte();
|
byte flg = in.readByte();
|
||||||
boolean keepProfilePrivilegesWhenDeviceAway = (flg & 0x10) != 0;
|
boolean notifyOnDeviceNearby = (flg & 0x10) != 0;
|
||||||
int userId = in.readInt();
|
int userId = in.readInt();
|
||||||
String deviceMacAddress = in.readString();
|
String deviceMacAddress = in.readString();
|
||||||
String packageName = in.readString();
|
String packageName = in.readString();
|
||||||
@@ -201,7 +201,7 @@ public final class Association implements Parcelable {
|
|||||||
com.android.internal.util.AnnotationValidations.validate(
|
com.android.internal.util.AnnotationValidations.validate(
|
||||||
NonNull.class, null, mPackageName);
|
NonNull.class, null, mPackageName);
|
||||||
this.mDeviceProfile = deviceProfile;
|
this.mDeviceProfile = deviceProfile;
|
||||||
this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
|
this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
|
||||||
|
|
||||||
// onConstructed(); // You can define this method to get a callback
|
// onConstructed(); // You can define this method to get a callback
|
||||||
}
|
}
|
||||||
@@ -221,10 +221,10 @@ public final class Association implements Parcelable {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@DataClass.Generated(
|
@DataClass.Generated(
|
||||||
time = 1606940835778L,
|
time = 1610482674799L,
|
||||||
codegenVersion = "1.0.21",
|
codegenVersion = "1.0.22",
|
||||||
sourceFile = "frameworks/base/core/java/android/companion/Association.java",
|
sourceFile = "frameworks/base/core/java/android/companion/Association.java",
|
||||||
inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mKeepProfilePrivilegesWhenDeviceAway\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
|
inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mNotifyOnDeviceNearby\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
|
||||||
@Deprecated
|
@Deprecated
|
||||||
private void __metadata() {}
|
private void __metadata() {}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,14 @@
|
|||||||
|
|
||||||
package com.android.server.companion;
|
package com.android.server.companion;
|
||||||
|
|
||||||
|
import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
|
||||||
|
import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
|
||||||
|
import static android.content.Context.BIND_IMPORTANT;
|
||||||
|
|
||||||
import static com.android.internal.util.CollectionUtils.emptyIfNull;
|
import static com.android.internal.util.CollectionUtils.emptyIfNull;
|
||||||
import static com.android.internal.util.CollectionUtils.find;
|
import static com.android.internal.util.CollectionUtils.find;
|
||||||
import static com.android.internal.util.CollectionUtils.forEach;
|
import static com.android.internal.util.CollectionUtils.forEach;
|
||||||
|
import static com.android.internal.util.CollectionUtils.map;
|
||||||
import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
|
import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
|
||||||
import static com.android.internal.util.Preconditions.checkArgument;
|
import static com.android.internal.util.Preconditions.checkArgument;
|
||||||
import static com.android.internal.util.Preconditions.checkNotNull;
|
import static com.android.internal.util.Preconditions.checkNotNull;
|
||||||
@@ -40,12 +45,18 @@ import android.app.PendingIntent;
|
|||||||
import android.app.role.RoleManager;
|
import android.app.role.RoleManager;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.le.ScanCallback;
|
||||||
|
import android.bluetooth.le.ScanFilter;
|
||||||
|
import android.bluetooth.le.ScanResult;
|
||||||
|
import android.bluetooth.le.ScanSettings;
|
||||||
import android.companion.Association;
|
import android.companion.Association;
|
||||||
import android.companion.AssociationRequest;
|
import android.companion.AssociationRequest;
|
||||||
import android.companion.CompanionDeviceManager;
|
import android.companion.CompanionDeviceManager;
|
||||||
|
import android.companion.CompanionDeviceService;
|
||||||
import android.companion.DeviceNotAssociatedException;
|
import android.companion.DeviceNotAssociatedException;
|
||||||
import android.companion.ICompanionDeviceDiscoveryService;
|
import android.companion.ICompanionDeviceDiscoveryService;
|
||||||
import android.companion.ICompanionDeviceManager;
|
import android.companion.ICompanionDeviceManager;
|
||||||
|
import android.companion.ICompanionDeviceService;
|
||||||
import android.companion.IFindDeviceCallback;
|
import android.companion.IFindDeviceCallback;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -77,6 +88,7 @@ import android.permission.PermissionControllerManager;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.provider.SettingsStringUtil.ComponentNameSet;
|
import android.provider.SettingsStringUtil.ComponentNameSet;
|
||||||
import android.text.BidiFormatter;
|
import android.text.BidiFormatter;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.AtomicFile;
|
import android.util.AtomicFile;
|
||||||
import android.util.ExceptionUtils;
|
import android.util.ExceptionUtils;
|
||||||
@@ -115,6 +127,7 @@ import java.io.PrintWriter;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -135,6 +148,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
|
CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
|
||||||
".DeviceDiscoveryService");
|
".DeviceDiscoveryService");
|
||||||
|
|
||||||
|
private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
|
||||||
|
private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;
|
||||||
|
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
private static final String LOG_TAG = "CompanionDeviceManagerService";
|
private static final String LOG_TAG = "CompanionDeviceManagerService";
|
||||||
|
|
||||||
@@ -146,18 +162,23 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
private static final String XML_ATTR_PACKAGE = "package";
|
private static final String XML_ATTR_PACKAGE = "package";
|
||||||
private static final String XML_ATTR_DEVICE = "device";
|
private static final String XML_ATTR_DEVICE = "device";
|
||||||
private static final String XML_ATTR_PROFILE = "profile";
|
private static final String XML_ATTR_PROFILE = "profile";
|
||||||
private static final String XML_ATTR_PERSISTENT_PROFILE_GRANTS = "persistent_profile_grants";
|
private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
|
||||||
private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
|
private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
|
||||||
|
|
||||||
private final CompanionDeviceManagerImpl mImpl;
|
private final CompanionDeviceManagerImpl mImpl;
|
||||||
private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
|
private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
|
||||||
private PowerWhitelistManager mPowerWhitelistManager;
|
private PowerWhitelistManager mPowerWhitelistManager;
|
||||||
private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
|
private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
|
||||||
|
/** userId -> packageName -> serviceConnector */
|
||||||
|
private PerUser<ArrayMap<String, ServiceConnector<ICompanionDeviceService>>>
|
||||||
|
mDeviceListenerServiceConnectors;
|
||||||
private IAppOpsService mAppOpsManager;
|
private IAppOpsService mAppOpsManager;
|
||||||
private RoleManager mRoleManager;
|
private RoleManager mRoleManager;
|
||||||
private BluetoothAdapter mBluetoothAdapter;
|
private BluetoothAdapter mBluetoothAdapter;
|
||||||
|
private UserManager mUserManager;
|
||||||
|
|
||||||
private IFindDeviceCallback mFindDeviceCallback;
|
private IFindDeviceCallback mFindDeviceCallback;
|
||||||
|
private ScanCallback mBleScanCallback = new BleScanCallback();
|
||||||
private AssociationRequest mRequest;
|
private AssociationRequest mRequest;
|
||||||
private String mCallingPackage;
|
private String mCallingPackage;
|
||||||
private AndroidFuture<Association> mOngoingDeviceDiscovery;
|
private AndroidFuture<Association> mOngoingDeviceDiscovery;
|
||||||
@@ -166,8 +187,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
|
private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
|
||||||
new BluetoothDeviceConnectedListener();
|
new BluetoothDeviceConnectedListener();
|
||||||
private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
|
private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
|
||||||
|
private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>();
|
||||||
|
private UnbindDeviceListenersRunnable
|
||||||
|
mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable();
|
||||||
|
private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables =
|
||||||
|
new ArrayMap<>();
|
||||||
|
|
||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
|
private final Handler mMainHandler = Handler.getMain();
|
||||||
|
|
||||||
/** userId -> [association] */
|
/** userId -> [association] */
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
@@ -189,6 +216,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
|
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
|
||||||
mPermissionControllerManager = requireNonNull(
|
mPermissionControllerManager = requireNonNull(
|
||||||
context.getSystemService(PermissionControllerManager.class));
|
context.getSystemService(PermissionControllerManager.class));
|
||||||
|
mUserManager = context.getSystemService(UserManager.class);
|
||||||
|
|
||||||
Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
|
Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
|
||||||
mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
|
mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
|
||||||
@@ -201,6 +229,16 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mDeviceListenerServiceConnectors = new PerUser<ArrayMap<String,
|
||||||
|
ServiceConnector<ICompanionDeviceService>>>() {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected ArrayMap<String, ServiceConnector<ICompanionDeviceService>> create(
|
||||||
|
int userId) {
|
||||||
|
return new ArrayMap<>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
registerPackageMonitor();
|
registerPackageMonitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,10 +246,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
new PackageMonitor() {
|
new PackageMonitor() {
|
||||||
@Override
|
@Override
|
||||||
public void onPackageRemoved(String packageName, int uid) {
|
public void onPackageRemoved(String packageName, int uid) {
|
||||||
|
int userId = getChangingUserId();
|
||||||
updateAssociations(
|
updateAssociations(
|
||||||
as -> CollectionUtils.filter(as,
|
as -> CollectionUtils.filter(as,
|
||||||
a -> !Objects.equals(a.getPackageName(), packageName)),
|
a -> !Objects.equals(a.getPackageName(), packageName)),
|
||||||
getChangingUserId());
|
userId);
|
||||||
|
|
||||||
|
unbindDevicePresenceListener(packageName, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -225,6 +266,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
}.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
|
}.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void unbindDevicePresenceListener(String packageName, int userId) {
|
||||||
|
ServiceConnector<ICompanionDeviceService> deviceListener =
|
||||||
|
mDeviceListenerServiceConnectors.forUser(userId)
|
||||||
|
.remove(packageName);
|
||||||
|
if (deviceListener != null) {
|
||||||
|
deviceListener.unbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
|
publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
|
||||||
@@ -238,6 +288,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
mBluetoothAdapter.registerBluetoothConnectionCallback(
|
mBluetoothAdapter.registerBluetoothConnectionCallback(
|
||||||
getContext().getMainExecutor(),
|
getContext().getMainExecutor(),
|
||||||
mBluetoothDeviceConnectedListener);
|
mBluetoothDeviceConnectedListener);
|
||||||
|
initBleScanning();
|
||||||
|
} else {
|
||||||
|
Log.w(LOG_TAG, "No BluetoothAdapter available");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,7 +340,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
Handler.getMain().post(this::cleanup);
|
mMainHandler.post(this::cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanup() {
|
private void cleanup() {
|
||||||
@@ -399,7 +452,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
checkCallerIsSystemOr(callingPackage, userId);
|
checkCallerIsSystemOr(callingPackage, userId);
|
||||||
checkUsesFeature(callingPackage, getCallingUserId());
|
checkUsesFeature(callingPackage, getCallingUserId());
|
||||||
}
|
}
|
||||||
return new ArrayList<>(CollectionUtils.map(
|
return new ArrayList<>(map(
|
||||||
getAllAssociations(userId, callingPackage),
|
getAllAssociations(userId, callingPackage),
|
||||||
a -> a.getDeviceMacAddress()));
|
a -> a.getDeviceMacAddress()));
|
||||||
}
|
}
|
||||||
@@ -506,22 +559,18 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
public void registerDevicePresenceListenerService(
|
public void registerDevicePresenceListenerService(
|
||||||
String packageName, String deviceAddress)
|
String packageName, String deviceAddress)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
checkCanRegisterObserverService(packageName, deviceAddress);
|
registerDevicePresenceListenerActive(packageName, deviceAddress, true);
|
||||||
|
|
||||||
//TODO(eugenesusla) implement
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unregisterDevicePresenceListenerService(
|
public void unregisterDevicePresenceListenerService(
|
||||||
String packageName, String deviceAddress)
|
String packageName, String deviceAddress)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
checkCanRegisterObserverService(packageName, deviceAddress);
|
registerDevicePresenceListenerActive(packageName, deviceAddress, false);
|
||||||
|
|
||||||
//TODO(eugenesusla) implement
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCanRegisterObserverService(String packageName, String deviceAddress)
|
private void registerDevicePresenceListenerActive(String packageName, String deviceAddress,
|
||||||
throws RemoteException {
|
boolean active) throws RemoteException {
|
||||||
getContext().enforceCallingOrSelfPermission(
|
getContext().enforceCallingOrSelfPermission(
|
||||||
android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE,
|
android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE,
|
||||||
"[un]registerDevicePresenceListenerService");
|
"[un]registerDevicePresenceListenerService");
|
||||||
@@ -537,6 +586,20 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
+ " is not associated with device " + deviceAddress
|
+ " is not associated with device " + deviceAddress
|
||||||
+ " for user " + userId));
|
+ " for user " + userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAssociations(associations -> map(associations, association -> {
|
||||||
|
if (Objects.equals(association.getPackageName(), packageName)
|
||||||
|
&& Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
|
||||||
|
return new Association(
|
||||||
|
association.getUserId(),
|
||||||
|
association.getDeviceMacAddress(),
|
||||||
|
association.getPackageName(),
|
||||||
|
association.getDeviceProfile(),
|
||||||
|
active /* notifyOnDeviceNearby */);
|
||||||
|
} else {
|
||||||
|
return association;
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
|
private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
|
||||||
@@ -693,6 +756,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
|
if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
|
||||||
grantDeviceProfile(association);
|
grantDeviceProfile(association);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (association.isNotifyOnDeviceNearby()) {
|
||||||
|
restartBleScan();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exemptFromAutoRevoke(String packageName, int uid) {
|
private void exemptFromAutoRevoke(String packageName, int uid) {
|
||||||
@@ -795,9 +862,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
association.getDeviceMacAddress());
|
association.getDeviceMacAddress());
|
||||||
if (association.getDeviceProfile() != null) {
|
if (association.getDeviceProfile() != null) {
|
||||||
tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile());
|
tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile());
|
||||||
tag.attribute(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS,
|
tag.attribute(null, XML_ATTR_NOTIFY_DEVICE_NEARBY,
|
||||||
Boolean.toString(
|
Boolean.toString(
|
||||||
association.isKeepProfilePrivilegesWhenDeviceAway()));
|
association.isNotifyOnDeviceNearby()));
|
||||||
}
|
}
|
||||||
tag.endTag(null, XML_TAG_ASSOCIATION);
|
tag.endTag(null, XML_TAG_ASSOCIATION);
|
||||||
});
|
});
|
||||||
@@ -845,6 +912,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
a -> Objects.equals(packageFilter, a.getPackageName()));
|
a -> Objects.equals(packageFilter, a.getPackageName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<Association> getAllAssociations() {
|
||||||
|
ArraySet<Association> result = new ArraySet<>();
|
||||||
|
for (UserInfo user : mUserManager.getAliveUsers()) {
|
||||||
|
result.addAll(getAllAssociations(user.id));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private Set<Association> readAllAssociations(int userId) {
|
private Set<Association> readAllAssociations(int userId) {
|
||||||
final AtomicFile file = getStorageFileForUser(userId);
|
final AtomicFile file = getStorageFileForUser(userId);
|
||||||
|
|
||||||
@@ -865,7 +940,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
|
|
||||||
final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE);
|
final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE);
|
||||||
final boolean persistentGrants = Boolean.valueOf(
|
final boolean persistentGrants = Boolean.valueOf(
|
||||||
parser.getAttributeValue(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS));
|
parser.getAttributeValue(null, XML_ATTR_NOTIFY_DEVICE_NEARBY));
|
||||||
|
|
||||||
if (appPackage == null || deviceAddress == null) continue;
|
if (appPackage == null || deviceAddress == null) continue;
|
||||||
|
|
||||||
@@ -896,6 +971,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDeviceNearby(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void grantDeviceProfile(Association association) {
|
private void grantDeviceProfile(Association association) {
|
||||||
@@ -919,6 +996,210 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
|
|
||||||
void onDeviceDisconnected(String address) {
|
void onDeviceDisconnected(String address) {
|
||||||
mCurrentlyConnectedDevices.remove(address);
|
mCurrentlyConnectedDevices.remove(address);
|
||||||
|
|
||||||
|
onDeviceDisappeared(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceConnector<ICompanionDeviceService> getDeviceListenerServiceConnector(
|
||||||
|
Association a) {
|
||||||
|
return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent(
|
||||||
|
a.getPackageName(),
|
||||||
|
pkg -> new ServiceConnector.Impl<>(getContext(),
|
||||||
|
new Intent(CompanionDeviceService.SERVICE_INTERFACE),
|
||||||
|
BIND_IMPORTANT,
|
||||||
|
a.getUserId(),
|
||||||
|
ICompanionDeviceService.Stub::asInterface));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BleScanCallback extends ScanCallback {
|
||||||
|
@Override
|
||||||
|
public void onScanResult(int callbackType, ScanResult result) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "onScanResult(callbackType = "
|
||||||
|
+ callbackType + ", result = " + result + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeviceNearby(result.getDevice().getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBatchScanResults(List<ScanResult> results) {
|
||||||
|
for (int i = 0, size = results.size(); i < size; i++) {
|
||||||
|
onScanResult(CALLBACK_TYPE_ALL_MATCHES, results.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScanFailed(int errorCode) {
|
||||||
|
if (errorCode == SCAN_FAILED_ALREADY_STARTED) {
|
||||||
|
// ignore - this might happen if BT tries to auto-restore scans for us in the
|
||||||
|
// future
|
||||||
|
} else {
|
||||||
|
Log.wtf(LOG_TAG, "Failed to start BLE scan: error " + errorCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UnbindDeviceListenersRunnable implements Runnable {
|
||||||
|
|
||||||
|
public String getJobId(String address) {
|
||||||
|
return "CDM_deviceGone_unbind_" + address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
int size = mDevicesLastNearby.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
String address = mDevicesLastNearby.keyAt(i);
|
||||||
|
Date lastNearby = mDevicesLastNearby.valueAt(i);
|
||||||
|
|
||||||
|
if (System.currentTimeMillis() - lastNearby.getTime()
|
||||||
|
>= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS) {
|
||||||
|
for (Association association : getAllAssociations(address)) {
|
||||||
|
if (association.isNotifyOnDeviceNearby()) {
|
||||||
|
getDeviceListenerServiceConnector(association).unbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TriggerDeviceDisappearedRunnable implements Runnable {
|
||||||
|
|
||||||
|
private final String mAddress;
|
||||||
|
|
||||||
|
TriggerDeviceDisappearedRunnable(String address) {
|
||||||
|
mAddress = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void schedule() {
|
||||||
|
mMainHandler.removeCallbacks(this);
|
||||||
|
mMainHandler.postDelayed(this, this, DEVICE_DISAPPEARED_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
onDeviceDisappeared(mAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Association> getAllAssociations(String deviceAddress) {
|
||||||
|
List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
|
||||||
|
Set<Association> result = new ArraySet<>();
|
||||||
|
for (int i = 0, size = aliveUsers.size(); i < size; i++) {
|
||||||
|
UserInfo user = aliveUsers.get(i);
|
||||||
|
for (Association association : getAllAssociations(user.id)) {
|
||||||
|
if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
|
||||||
|
result.add(association);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDeviceNearby(String address) {
|
||||||
|
Date timestamp = new Date();
|
||||||
|
mDevicesLastNearby.put(address, timestamp);
|
||||||
|
|
||||||
|
cancelUnbindDeviceListener(address);
|
||||||
|
|
||||||
|
mTriggerDeviceDisappearedRunnables
|
||||||
|
.computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address))
|
||||||
|
.schedule();
|
||||||
|
|
||||||
|
for (Association association : getAllAssociations(address)) {
|
||||||
|
if (association.isNotifyOnDeviceNearby()) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "Device " + address
|
||||||
|
+ " managed by " + association.getPackageName()
|
||||||
|
+ " is nearby on " + timestamp);
|
||||||
|
}
|
||||||
|
getDeviceListenerServiceConnector(association).run(
|
||||||
|
service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDeviceDisappeared(String address) {
|
||||||
|
boolean hasDeviceListeners = false;
|
||||||
|
for (Association association : getAllAssociations(address)) {
|
||||||
|
if (association.isNotifyOnDeviceNearby()) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(LOG_TAG, "Device " + address
|
||||||
|
+ " managed by " + association.getPackageName()
|
||||||
|
+ " disappeared; last seen on " + mDevicesLastNearby.get(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeviceListenerServiceConnector(association).run(
|
||||||
|
service -> service.onDeviceDisappeared(address));
|
||||||
|
hasDeviceListeners = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelUnbindDeviceListener(address);
|
||||||
|
if (hasDeviceListeners) {
|
||||||
|
mMainHandler.postDelayed(
|
||||||
|
mUnbindDeviceListenersRunnable,
|
||||||
|
mUnbindDeviceListenersRunnable.getJobId(address),
|
||||||
|
DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelUnbindDeviceListener(String address) {
|
||||||
|
mMainHandler.removeCallbacks(
|
||||||
|
mUnbindDeviceListenersRunnable, mUnbindDeviceListenersRunnable.getJobId(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initBleScanning() {
|
||||||
|
boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback(
|
||||||
|
new BluetoothAdapter.ServiceLifecycleCallback() {
|
||||||
|
@Override
|
||||||
|
public void onBluetoothServiceUp() {
|
||||||
|
Log.i(LOG_TAG, "Bluetooth stack is up");
|
||||||
|
startBleScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBluetoothServiceDown() {
|
||||||
|
Log.w(LOG_TAG, "Bluetooth stack is down");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (bluetoothReady) {
|
||||||
|
startBleScan();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startBleScan() {
|
||||||
|
List<ScanFilter> filters = getBleScanFilters();
|
||||||
|
if (filters.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mBluetoothAdapter.getBluetoothLeScanner().startScan(
|
||||||
|
filters,
|
||||||
|
new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
|
||||||
|
mBleScanCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void restartBleScan() {
|
||||||
|
mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback);
|
||||||
|
startBleScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ScanFilter> getBleScanFilters() {
|
||||||
|
ArrayList<ScanFilter> result = new ArrayList<>();
|
||||||
|
ArraySet<String> addressesSeen = new ArraySet<>();
|
||||||
|
for (Association association : getAllAssociations()) {
|
||||||
|
String address = association.getDeviceMacAddress();
|
||||||
|
if (addressesSeen.contains(address)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (association.isNotifyOnDeviceNearby()) {
|
||||||
|
result.add(new ScanFilter.Builder().setDeviceAddress(address).build());
|
||||||
|
addressesSeen.add(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) {
|
private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) {
|
||||||
|
|||||||
Reference in New Issue
Block a user