Store CDM device profile and apply role when device is connected
Test: manual - ensure role privileges are granted/revoked when device is connected/disconnected Bug: 165951651 Change-Id: Id24a4b3a3510781d9105763b1722f44583a7fd7c
This commit is contained in:
@@ -3689,7 +3689,7 @@ public final class BluetoothAdapter {
|
|||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public abstract class BluetoothConnectionCallback {
|
public abstract static class BluetoothConnectionCallback {
|
||||||
/**
|
/**
|
||||||
* Callback triggered when a bluetooth device (classic or BLE) is connected
|
* Callback triggered when a bluetooth device (classic or BLE) is connected
|
||||||
* @param device is the connected bluetooth device
|
* @param device is the connected bluetooth device
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public final class Association implements Parcelable {
|
|||||||
private final @UserIdInt int mUserId;
|
private final @UserIdInt int mUserId;
|
||||||
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 boolean mKeepProfilePrivilegesWhenDeviceAway;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public int getUserId() {
|
public int getUserId() {
|
||||||
@@ -45,7 +47,7 @@ public final class Association implements Parcelable {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Code below generated by codegen v1.0.15.
|
// Code below generated by codegen v1.0.21.
|
||||||
//
|
//
|
||||||
// DO NOT MODIFY!
|
// DO NOT MODIFY!
|
||||||
// CHECKSTYLE:OFF Generated code
|
// CHECKSTYLE:OFF Generated code
|
||||||
@@ -67,7 +69,9 @@ public final class Association implements Parcelable {
|
|||||||
public Association(
|
public Association(
|
||||||
@UserIdInt int userId,
|
@UserIdInt int userId,
|
||||||
@NonNull String deviceMacAddress,
|
@NonNull String deviceMacAddress,
|
||||||
@NonNull String packageName) {
|
@NonNull String packageName,
|
||||||
|
@Nullable String deviceProfile,
|
||||||
|
boolean keepProfilePrivilegesWhenDeviceAway) {
|
||||||
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);
|
||||||
@@ -77,6 +81,8 @@ public final class Association implements Parcelable {
|
|||||||
this.mPackageName = packageName;
|
this.mPackageName = packageName;
|
||||||
com.android.internal.util.AnnotationValidations.validate(
|
com.android.internal.util.AnnotationValidations.validate(
|
||||||
NonNull.class, null, mPackageName);
|
NonNull.class, null, mPackageName);
|
||||||
|
this.mDeviceProfile = deviceProfile;
|
||||||
|
this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
|
||||||
|
|
||||||
// onConstructed(); // You can define this method to get a callback
|
// onConstructed(); // You can define this method to get a callback
|
||||||
}
|
}
|
||||||
@@ -91,6 +97,16 @@ public final class Association implements Parcelable {
|
|||||||
return mPackageName;
|
return mPackageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DataClass.Generated.Member
|
||||||
|
public @Nullable String getDeviceProfile() {
|
||||||
|
return mDeviceProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DataClass.Generated.Member
|
||||||
|
public boolean isKeepProfilePrivilegesWhenDeviceAway() {
|
||||||
|
return mKeepProfilePrivilegesWhenDeviceAway;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@DataClass.Generated.Member
|
@DataClass.Generated.Member
|
||||||
public String toString() {
|
public String toString() {
|
||||||
@@ -100,7 +116,9 @@ public final class Association implements Parcelable {
|
|||||||
return "Association { " +
|
return "Association { " +
|
||||||
"userId = " + mUserId + ", " +
|
"userId = " + mUserId + ", " +
|
||||||
"deviceMacAddress = " + mDeviceMacAddress + ", " +
|
"deviceMacAddress = " + mDeviceMacAddress + ", " +
|
||||||
"packageName = " + mPackageName +
|
"packageName = " + mPackageName + ", " +
|
||||||
|
"deviceProfile = " + mDeviceProfile + ", " +
|
||||||
|
"keepProfilePrivilegesWhenDeviceAway = " + mKeepProfilePrivilegesWhenDeviceAway +
|
||||||
" }";
|
" }";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +137,9 @@ public final class Association implements Parcelable {
|
|||||||
return true
|
return true
|
||||||
&& mUserId == that.mUserId
|
&& mUserId == that.mUserId
|
||||||
&& 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)
|
||||||
|
&& mKeepProfilePrivilegesWhenDeviceAway == that.mKeepProfilePrivilegesWhenDeviceAway;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,6 +152,8 @@ public final class Association implements Parcelable {
|
|||||||
_hash = 31 * _hash + mUserId;
|
_hash = 31 * _hash + mUserId;
|
||||||
_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 + Boolean.hashCode(mKeepProfilePrivilegesWhenDeviceAway);
|
||||||
return _hash;
|
return _hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,9 +163,14 @@ public final class Association implements Parcelable {
|
|||||||
// You can override field parcelling by defining methods like:
|
// You can override field parcelling by defining methods like:
|
||||||
// void parcelFieldName(Parcel dest, int flags) { ... }
|
// void parcelFieldName(Parcel dest, int flags) { ... }
|
||||||
|
|
||||||
|
byte flg = 0;
|
||||||
|
if (mKeepProfilePrivilegesWhenDeviceAway) flg |= 0x10;
|
||||||
|
if (mDeviceProfile != null) flg |= 0x8;
|
||||||
|
dest.writeByte(flg);
|
||||||
dest.writeInt(mUserId);
|
dest.writeInt(mUserId);
|
||||||
dest.writeString(mDeviceMacAddress);
|
dest.writeString(mDeviceMacAddress);
|
||||||
dest.writeString(mPackageName);
|
dest.writeString(mPackageName);
|
||||||
|
if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -157,9 +184,12 @@ public final class Association implements Parcelable {
|
|||||||
// You can override field unparcelling by defining methods like:
|
// You can override field unparcelling by defining methods like:
|
||||||
// static FieldType unparcelFieldName(Parcel in) { ... }
|
// static FieldType unparcelFieldName(Parcel in) { ... }
|
||||||
|
|
||||||
|
byte flg = in.readByte();
|
||||||
|
boolean keepProfilePrivilegesWhenDeviceAway = (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();
|
||||||
|
String deviceProfile = (flg & 0x8) == 0 ? null : in.readString();
|
||||||
|
|
||||||
this.mUserId = userId;
|
this.mUserId = userId;
|
||||||
com.android.internal.util.AnnotationValidations.validate(
|
com.android.internal.util.AnnotationValidations.validate(
|
||||||
@@ -170,6 +200,8 @@ public final class Association implements Parcelable {
|
|||||||
this.mPackageName = packageName;
|
this.mPackageName = packageName;
|
||||||
com.android.internal.util.AnnotationValidations.validate(
|
com.android.internal.util.AnnotationValidations.validate(
|
||||||
NonNull.class, null, mPackageName);
|
NonNull.class, null, mPackageName);
|
||||||
|
this.mDeviceProfile = deviceProfile;
|
||||||
|
this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
|
||||||
|
|
||||||
// onConstructed(); // You can define this method to get a callback
|
// onConstructed(); // You can define this method to get a callback
|
||||||
}
|
}
|
||||||
@@ -189,10 +221,10 @@ public final class Association implements Parcelable {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@DataClass.Generated(
|
@DataClass.Generated(
|
||||||
time = 1599083149942L,
|
time = 1606940835778L,
|
||||||
codegenVersion = "1.0.15",
|
codegenVersion = "1.0.21",
|
||||||
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\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 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)")
|
||||||
@Deprecated
|
@Deprecated
|
||||||
private void __metadata() {}
|
private void __metadata() {}
|
||||||
|
|
||||||
|
|||||||
@@ -321,7 +321,8 @@ public class DeviceDiscoveryService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onDeviceSelected(String callingPackage, String deviceAddress) {
|
void onDeviceSelected(String callingPackage, String deviceAddress) {
|
||||||
mServiceCallback.complete(new Association(getUserId(), deviceAddress, callingPackage));
|
mServiceCallback.complete(new Association(
|
||||||
|
getUserId(), deviceAddress, callingPackage, mRequest.getDeviceProfile(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onCancel() {
|
void onCancel() {
|
||||||
|
|||||||
@@ -31,8 +31,12 @@ import static java.util.concurrent.TimeUnit.MINUTES;
|
|||||||
import android.annotation.CheckResult;
|
import android.annotation.CheckResult;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.app.role.RoleManager;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.companion.Association;
|
import android.companion.Association;
|
||||||
import android.companion.AssociationRequest;
|
import android.companion.AssociationRequest;
|
||||||
import android.companion.CompanionDeviceManager;
|
import android.companion.CompanionDeviceManager;
|
||||||
@@ -47,6 +51,7 @@ import android.content.pm.FeatureInfo;
|
|||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageItemInfo;
|
import android.content.pm.PackageItemInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.UserInfo;
|
||||||
import android.net.NetworkPolicyManager;
|
import android.net.NetworkPolicyManager;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
@@ -62,6 +67,7 @@ import android.os.ServiceManager;
|
|||||||
import android.os.ShellCallback;
|
import android.os.ShellCallback;
|
||||||
import android.os.ShellCommand;
|
import android.os.ShellCommand;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.os.UserManager;
|
||||||
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;
|
||||||
@@ -70,6 +76,7 @@ import android.util.AtomicFile;
|
|||||||
import android.util.ExceptionUtils;
|
import android.util.ExceptionUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
import android.util.SparseArray;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
@@ -116,12 +123,16 @@ import java.util.function.Function;
|
|||||||
//TODO on associate called again after configuration change -> replace old callback with new
|
//TODO on associate called again after configuration change -> replace old callback with new
|
||||||
//TODO avoid leaking calling activity in IFindDeviceCallback (see PrintManager#print for example)
|
//TODO avoid leaking calling activity in IFindDeviceCallback (see PrintManager#print for example)
|
||||||
/** @hide */
|
/** @hide */
|
||||||
|
@SuppressLint("LongLogTag")
|
||||||
public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient {
|
public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient {
|
||||||
|
|
||||||
private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative(
|
private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative(
|
||||||
CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
|
CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
|
||||||
".DeviceDiscoveryService");
|
".DeviceDiscoveryService");
|
||||||
|
|
||||||
|
// 10 min
|
||||||
|
public static final int DEVICE_DISCONNECT_PROFILE_REVOKE_DELAY_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";
|
||||||
|
|
||||||
@@ -132,6 +143,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
private static final String XML_TAG_ASSOCIATION = "association";
|
private static final String XML_TAG_ASSOCIATION = "association";
|
||||||
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_PERSISTENT_PROFILE_GRANTS = "persistent_profile_grants";
|
||||||
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;
|
||||||
@@ -139,21 +152,29 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
private PowerWhitelistManager mPowerWhitelistManager;
|
private PowerWhitelistManager mPowerWhitelistManager;
|
||||||
private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
|
private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
|
||||||
private IAppOpsService mAppOpsManager;
|
private IAppOpsService mAppOpsManager;
|
||||||
|
private RoleManager mRoleManager;
|
||||||
|
private BluetoothAdapter mBluetoothAdapter;
|
||||||
|
|
||||||
private IFindDeviceCallback mFindDeviceCallback;
|
private IFindDeviceCallback mFindDeviceCallback;
|
||||||
private AssociationRequest mRequest;
|
private AssociationRequest mRequest;
|
||||||
private String mCallingPackage;
|
private String mCallingPackage;
|
||||||
private AndroidFuture<Association> mOngoingDeviceDiscovery;
|
private AndroidFuture<Association> mOngoingDeviceDiscovery;
|
||||||
|
|
||||||
|
private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
|
||||||
|
new BluetoothDeviceConnectedListener();
|
||||||
|
private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
|
||||||
|
|
||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
|
/** userId -> [association] */
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private @Nullable Set<Association> mCachedAssociations = null;
|
private @Nullable SparseArray<Set<Association>> mCachedAssociations = new SparseArray<>();
|
||||||
|
|
||||||
public CompanionDeviceManagerService(Context context) {
|
public CompanionDeviceManagerService(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
mImpl = new CompanionDeviceManagerImpl();
|
mImpl = new CompanionDeviceManagerImpl();
|
||||||
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
|
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
|
||||||
|
mRoleManager = context.getSystemService(RoleManager.class);
|
||||||
mAppOpsManager = IAppOpsService.Stub.asInterface(
|
mAppOpsManager = IAppOpsService.Stub.asInterface(
|
||||||
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
||||||
|
|
||||||
@@ -184,9 +205,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
@Override
|
@Override
|
||||||
public void onPackageModified(String packageName) {
|
public void onPackageModified(String packageName) {
|
||||||
int userId = getChangingUserId();
|
int userId = getChangingUserId();
|
||||||
if (!ArrayUtils.isEmpty(getAllAssociations(userId, packageName))) {
|
forEach(getAllAssociations(userId, packageName), association -> {
|
||||||
updateSpecialAccessPermissionForAssociatedPackage(packageName, userId);
|
updateSpecialAccessPermissionForAssociatedPackage(association);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
|
}.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
|
||||||
@@ -197,6 +218,18 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
|
publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBootPhase(int phase) {
|
||||||
|
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
|
||||||
|
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
if (mBluetoothAdapter != null) {
|
||||||
|
mBluetoothAdapter.registerBluetoothConnectionCallback(
|
||||||
|
getContext().getMainExecutor(),
|
||||||
|
mBluetoothDeviceConnectedListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUserUnlocking(@NonNull TargetUser user) {
|
public void onUserUnlocking(@NonNull TargetUser user) {
|
||||||
int userHandle = user.getUserIdentifier();
|
int userHandle = user.getUserIdentifier();
|
||||||
@@ -495,12 +528,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
|
|
||||||
fout.append("Companion Device Associations:").append('\n');
|
fout.append("Companion Device Associations:").append('\n');
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
forEach(mCachedAssociations, a -> {
|
for (UserInfo user : getAllUsers()) {
|
||||||
fout.append(" ")
|
forEach(mCachedAssociations.get(user.id), a -> {
|
||||||
.append("u").append("" + a.getUserId()).append(": ")
|
fout.append(" ")
|
||||||
.append(a.getPackageName()).append(" - ")
|
.append("u").append("" + a.getUserId()).append(": ")
|
||||||
.append(a.getDeviceMacAddress()).append('\n');
|
.append(a.getPackageName()).append(" - ")
|
||||||
});
|
.append(a.getDeviceMacAddress()).append('\n');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -513,32 +548,34 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
return Binder.getCallingUid() == Process.SYSTEM_UID;
|
return Binder.getCallingUid() == Process.SYSTEM_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addAssociation(int userId, String packageName, String deviceAddress) {
|
|
||||||
addAssociation(new Association(userId, deviceAddress, packageName));
|
|
||||||
}
|
|
||||||
|
|
||||||
void addAssociation(Association association) {
|
void addAssociation(Association association) {
|
||||||
updateSpecialAccessPermissionForAssociatedPackage(
|
updateSpecialAccessPermissionForAssociatedPackage(association);
|
||||||
association.getPackageName(), association.getUserId());
|
|
||||||
recordAssociation(association);
|
recordAssociation(association);
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeAssociation(int userId, String pkg, String deviceMacAddress) {
|
void removeAssociation(int userId, String pkg, String deviceMacAddress) {
|
||||||
updateAssociations(associations -> CollectionUtils.remove(associations,
|
updateAssociations(associations -> CollectionUtils.filter(associations, association -> {
|
||||||
new Association(userId, deviceMacAddress, pkg)));
|
return association.getUserId() != userId
|
||||||
|
|| !Objects.equals(association.getDeviceMacAddress(), deviceMacAddress)
|
||||||
|
|| !Objects.equals(association.getPackageName(), pkg);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSpecialAccessPermissionForAssociatedPackage(String packageName, int userId) {
|
private void updateSpecialAccessPermissionForAssociatedPackage(Association association) {
|
||||||
PackageInfo packageInfo = getPackageInfo(packageName, userId);
|
PackageInfo packageInfo = getPackageInfo(
|
||||||
|
association.getPackageName(),
|
||||||
|
association.getUserId());
|
||||||
if (packageInfo == null) {
|
if (packageInfo == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Binder.withCleanCallingIdentity(obtainRunnable(CompanionDeviceManagerService::
|
Binder.withCleanCallingIdentity(obtainRunnable(CompanionDeviceManagerService::
|
||||||
updateSpecialAccessPermissionAsSystem, this, packageInfo).recycleOnUse());
|
updateSpecialAccessPermissionAsSystem, this, association, packageInfo)
|
||||||
|
.recycleOnUse());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo) {
|
private void updateSpecialAccessPermissionAsSystem(
|
||||||
|
Association association, PackageInfo packageInfo) {
|
||||||
if (containsEither(packageInfo.requestedPermissions,
|
if (containsEither(packageInfo.requestedPermissions,
|
||||||
android.Manifest.permission.RUN_IN_BACKGROUND,
|
android.Manifest.permission.RUN_IN_BACKGROUND,
|
||||||
android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
|
android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
|
||||||
@@ -602,10 +639,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
updateAssociations(associations -> CollectionUtils.add(associations, association));
|
updateAssociations(associations -> CollectionUtils.add(associations, association));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recordAssociation(String privilegedPackage, String deviceAddress) {
|
|
||||||
recordAssociation(new Association(getCallingUserId(), deviceAddress, privilegedPackage));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateAssociations(Function<Set<Association>, Set<Association>> update) {
|
private void updateAssociations(Function<Set<Association>, Set<Association>> update) {
|
||||||
updateAssociations(update, getCallingUserId());
|
updateAssociations(update, getCallingUserId());
|
||||||
}
|
}
|
||||||
@@ -625,7 +658,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.i(LOG_TAG, "Updating associations: " + old + " --> " + associations);
|
Slog.i(LOG_TAG, "Updating associations: " + old + " --> " + associations);
|
||||||
}
|
}
|
||||||
mCachedAssociations = Collections.unmodifiableSet(associations);
|
mCachedAssociations.put(userId, Collections.unmodifiableSet(associations));
|
||||||
BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
|
BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
|
||||||
CompanionDeviceManagerService::persistAssociations,
|
CompanionDeviceManagerService::persistAssociations,
|
||||||
this, associations, userId));
|
this, associations, userId));
|
||||||
@@ -651,10 +684,17 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
xml.startTag(null, XML_TAG_ASSOCIATIONS);
|
xml.startTag(null, XML_TAG_ASSOCIATIONS);
|
||||||
|
|
||||||
forEach(associations, association -> {
|
forEach(associations, association -> {
|
||||||
xml.startTag(null, XML_TAG_ASSOCIATION)
|
XmlSerializer tag = xml.startTag(null, XML_TAG_ASSOCIATION)
|
||||||
.attribute(null, XML_ATTR_PACKAGE, association.getPackageName())
|
.attribute(null, XML_ATTR_PACKAGE, association.getPackageName())
|
||||||
.attribute(null, XML_ATTR_DEVICE, association.getDeviceMacAddress())
|
.attribute(null, XML_ATTR_DEVICE,
|
||||||
.endTag(null, XML_TAG_ASSOCIATION);
|
association.getDeviceMacAddress());
|
||||||
|
if (association.getDeviceProfile() != null) {
|
||||||
|
tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile());
|
||||||
|
tag.attribute(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS,
|
||||||
|
Boolean.toString(
|
||||||
|
association.isKeepProfilePrivilegesWhenDeviceAway()));
|
||||||
|
}
|
||||||
|
tag.endTag(null, XML_TAG_ASSOCIATION);
|
||||||
});
|
});
|
||||||
|
|
||||||
xml.endTag(null, XML_TAG_ASSOCIATIONS);
|
xml.endTag(null, XML_TAG_ASSOCIATIONS);
|
||||||
@@ -678,17 +718,21 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
@Nullable
|
@Nullable
|
||||||
private Set<Association> getAllAssociations(int userId) {
|
private Set<Association> getAllAssociations(int userId) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mCachedAssociations == null) {
|
if (mCachedAssociations.get(userId) == null) {
|
||||||
mCachedAssociations = Collections.unmodifiableSet(
|
mCachedAssociations.put(userId, Collections.unmodifiableSet(
|
||||||
emptyIfNull(readAllAssociations(userId)));
|
emptyIfNull(readAllAssociations(userId))));
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.i(LOG_TAG, "Read associations from disk: " + mCachedAssociations);
|
Slog.i(LOG_TAG, "Read associations from disk: " + mCachedAssociations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mCachedAssociations;
|
return mCachedAssociations.get(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<UserInfo> getAllUsers() {
|
||||||
|
return getContext().getSystemService(UserManager.class).getUsers();
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
|
private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
|
||||||
return CollectionUtils.filter(
|
return CollectionUtils.filter(
|
||||||
@@ -714,10 +758,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
final String appPackage = parser.getAttributeValue(null, XML_ATTR_PACKAGE);
|
final String appPackage = parser.getAttributeValue(null, XML_ATTR_PACKAGE);
|
||||||
final String deviceAddress = parser.getAttributeValue(null, XML_ATTR_DEVICE);
|
final String deviceAddress = parser.getAttributeValue(null, XML_ATTR_DEVICE);
|
||||||
|
|
||||||
|
final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE);
|
||||||
|
final boolean persistentGrants = Boolean.valueOf(
|
||||||
|
parser.getAttributeValue(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS));
|
||||||
|
|
||||||
if (appPackage == null || deviceAddress == null) continue;
|
if (appPackage == null || deviceAddress == null) continue;
|
||||||
|
|
||||||
result = ArrayUtils.add(result,
|
result = ArrayUtils.add(result,
|
||||||
new Association(userId, deviceAddress, appPackage));
|
new Association(userId, deviceAddress, appPackage,
|
||||||
|
profile, persistentGrants));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} catch (XmlPullParserException | IOException e) {
|
} catch (XmlPullParserException | IOException e) {
|
||||||
@@ -727,6 +776,77 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onDeviceConnected(String address) {
|
||||||
|
mCurrentlyConnectedDevices.add(address);
|
||||||
|
|
||||||
|
Handler.getMain().removeCallbacksAndMessages(getDisconnectJobHandlerId(address));
|
||||||
|
|
||||||
|
for (UserInfo user : getAllUsers()) {
|
||||||
|
for (Association association : getAllAssociations(user.id)) {
|
||||||
|
if (Objects.equals(address, association.getDeviceMacAddress())) {
|
||||||
|
if (association.getDeviceProfile() != null) {
|
||||||
|
Log.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
|
||||||
|
+ " to " + association.getPackageName()
|
||||||
|
+ " due to device connected: " + address);
|
||||||
|
mRoleManager.addRoleHolderAsUser(
|
||||||
|
association.getDeviceProfile(),
|
||||||
|
association.getPackageName(),
|
||||||
|
RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP,
|
||||||
|
UserHandle.of(association.getUserId()),
|
||||||
|
getContext().getMainExecutor(),
|
||||||
|
success -> {
|
||||||
|
if (!success) {
|
||||||
|
Log.e(LOG_TAG, "Failed to grant device profile role "
|
||||||
|
+ association.getDeviceProfile()
|
||||||
|
+ " to " + association.getPackageName()
|
||||||
|
+ " for user " + association.getUserId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDeviceDisconnected(String address) {
|
||||||
|
mCurrentlyConnectedDevices.remove(address);
|
||||||
|
|
||||||
|
Handler.getMain().postDelayed(() -> {
|
||||||
|
if (!mCurrentlyConnectedDevices.contains(address)) {
|
||||||
|
for (UserInfo user : getAllUsers()) {
|
||||||
|
for (Association association : getAllAssociations(user.id)) {
|
||||||
|
if (association.getDeviceProfile() != null
|
||||||
|
&& Objects.equals(address, association.getDeviceMacAddress())
|
||||||
|
&& !association.isKeepProfilePrivilegesWhenDeviceAway()) {
|
||||||
|
Log.i(LOG_TAG, "Revoking role " + association.getDeviceProfile()
|
||||||
|
+ " to " + association.getPackageName()
|
||||||
|
+ " due to device disconnected: " + address);
|
||||||
|
mRoleManager.removeRoleHolderAsUser(
|
||||||
|
association.getDeviceProfile(),
|
||||||
|
association.getPackageName(),
|
||||||
|
RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP,
|
||||||
|
UserHandle.of(association.getUserId()),
|
||||||
|
getContext().getMainExecutor(),
|
||||||
|
success -> {
|
||||||
|
if (!success) {
|
||||||
|
Log.e(LOG_TAG, "Failed to revoke device profile role "
|
||||||
|
+ association.getDeviceProfile()
|
||||||
|
+ " to " + association.getPackageName()
|
||||||
|
+ " for user " + association.getUserId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, getDisconnectJobHandlerId(address), DEVICE_DISCONNECT_PROFILE_REVOKE_DELAY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String getDisconnectJobHandlerId(String address) {
|
||||||
|
return "CDM_onDisconnected_" + address;
|
||||||
|
}
|
||||||
|
|
||||||
private class ShellCmd extends ShellCommand {
|
private class ShellCmd extends ShellCommand {
|
||||||
public static final String USAGE = "help\n"
|
public static final String USAGE = "help\n"
|
||||||
+ "list USER_ID\n"
|
+ "list USER_ID\n"
|
||||||
@@ -749,13 +869,22 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case "associate": {
|
case "associate": {
|
||||||
addAssociation(getNextArgInt(), getNextArgRequired(), getNextArgRequired());
|
addAssociation(new Association(getNextArgInt(), getNextArgRequired(),
|
||||||
|
getNextArgRequired(), null, false));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case "disassociate": {
|
case "disassociate": {
|
||||||
removeAssociation(getNextArgInt(), getNextArgRequired(), getNextArgRequired());
|
removeAssociation(getNextArgInt(), getNextArgRequired(), getNextArgRequired());
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case "simulate_connect": {
|
||||||
|
onDeviceConnected(getNextArgRequired());
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case "simulate_disconnect": {
|
||||||
|
onDeviceDisconnected(getNextArgRequired());
|
||||||
|
} break;
|
||||||
|
|
||||||
default: return handleDefaultCommands(cmd);
|
default: return handleDefaultCommands(cmd);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -771,4 +900,17 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class BluetoothDeviceConnectedListener
|
||||||
|
extends BluetoothAdapter.BluetoothConnectionCallback {
|
||||||
|
@Override
|
||||||
|
public void onDeviceConnected(BluetoothDevice device) {
|
||||||
|
CompanionDeviceManagerService.this.onDeviceConnected(device.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceDisconnected(BluetoothDevice device) {
|
||||||
|
CompanionDeviceManagerService.this.onDeviceDisconnected(device.getAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user