Add an API to set if ContextSync is enabled
Allow companion apps to control whether ContextSync is enabled. By default it's opted-in/enabled. Bug: 261885804 Test: CTS tests Change-Id: Ib77726a441435511ae87c0e4f9f73c421c1c7acf
This commit is contained in:
@@ -9178,6 +9178,7 @@ package android.companion {
|
||||
method @Nullable public String getDeviceProfile();
|
||||
method @Nullable public CharSequence getDisplayName();
|
||||
method public int getId();
|
||||
method public int getSystemDataSyncFlags();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.companion.AssociationInfo> CREATOR;
|
||||
}
|
||||
@@ -9247,8 +9248,10 @@ package android.companion {
|
||||
method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.Callback);
|
||||
method @Nullable public android.content.IntentSender buildAssociationCancellationIntent();
|
||||
method @Nullable public android.content.IntentSender buildPermissionTransferUserConsentIntent(int) throws android.companion.DeviceNotAssociatedException;
|
||||
method public void disableSystemDataSync(int, int);
|
||||
method @Deprecated public void disassociate(@NonNull String);
|
||||
method public void disassociate(int);
|
||||
method public void enableSystemDataSync(int, int);
|
||||
method @NonNull @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public java.util.List<android.companion.AssociationInfo> getAllAssociations();
|
||||
method @Deprecated @NonNull public java.util.List<java.lang.String> getAssociations();
|
||||
method @NonNull public java.util.List<android.companion.AssociationInfo> getMyAssociations();
|
||||
@@ -9259,6 +9262,7 @@ package android.companion {
|
||||
method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
|
||||
field public static final String EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION";
|
||||
field @Deprecated public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
|
||||
field public static final int FLAG_CALL_METADATA = 1; // 0x1
|
||||
field public static final int RESULT_CANCELED = 0; // 0x0
|
||||
field public static final int RESULT_DISCOVERY_TIMEOUT = 2; // 0x2
|
||||
field public static final int RESULT_INTERNAL_ERROR = 3; // 0x3
|
||||
|
||||
@@ -56,6 +56,7 @@ public final class AssociationInfo implements Parcelable {
|
||||
|
||||
private final boolean mSelfManaged;
|
||||
private final boolean mNotifyOnDeviceNearby;
|
||||
private final int mSystemDataSyncFlags;
|
||||
|
||||
/**
|
||||
* Indicates that the association has been revoked (removed), but we keep the association
|
||||
@@ -73,7 +74,6 @@ public final class AssociationInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* Creates a new Association.
|
||||
* Only to be used by the CompanionDeviceManagerService.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@@ -81,7 +81,7 @@ public final class AssociationInfo implements Parcelable {
|
||||
@Nullable MacAddress macAddress, @Nullable CharSequence displayName,
|
||||
@Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
|
||||
boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked,
|
||||
long timeApprovedMs, long lastTimeConnectedMs) {
|
||||
long timeApprovedMs, long lastTimeConnectedMs, int systemDataSyncFlags) {
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException("Association ID should be greater than 0");
|
||||
}
|
||||
@@ -105,6 +105,7 @@ public final class AssociationInfo implements Parcelable {
|
||||
mRevoked = revoked;
|
||||
mTimeApprovedMs = timeApprovedMs;
|
||||
mLastTimeConnectedMs = lastTimeConnectedMs;
|
||||
mSystemDataSyncFlags = systemDataSyncFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,6 +221,16 @@ public final class AssociationInfo implements Parcelable {
|
||||
return mLastTimeConnectedMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Enabled system data sync flags set via
|
||||
* {@link CompanionDeviceManager#enableSystemDataSync(int, int)} and
|
||||
* {@link CompanionDeviceManager#disableSystemDataSync(int, int)}.
|
||||
* Or by default all flags are 1 (enabled).
|
||||
*/
|
||||
public int getSystemDataSyncFlags() {
|
||||
return mSystemDataSyncFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for checking if the association represents a device with the given MAC
|
||||
* address.
|
||||
@@ -287,6 +298,7 @@ public final class AssociationInfo implements Parcelable {
|
||||
+ ", mLastTimeConnectedMs=" + (
|
||||
mLastTimeConnectedMs == Long.MAX_VALUE
|
||||
? LAST_TIME_CONNECTED_NONE : new Date(mLastTimeConnectedMs))
|
||||
+ ", mSystemDataSyncFlags=" + mSystemDataSyncFlags
|
||||
+ '}';
|
||||
}
|
||||
|
||||
@@ -306,14 +318,15 @@ public final class AssociationInfo implements Parcelable {
|
||||
&& Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
|
||||
&& Objects.equals(mDisplayName, that.mDisplayName)
|
||||
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
|
||||
&& Objects.equals(mAssociatedDevice, that.mAssociatedDevice);
|
||||
&& Objects.equals(mAssociatedDevice, that.mAssociatedDevice)
|
||||
&& mSystemDataSyncFlags == that.mSystemDataSyncFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
|
||||
mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked,
|
||||
mTimeApprovedMs, mLastTimeConnectedMs);
|
||||
mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -338,6 +351,7 @@ public final class AssociationInfo implements Parcelable {
|
||||
dest.writeBoolean(mRevoked);
|
||||
dest.writeLong(mTimeApprovedMs);
|
||||
dest.writeLong(mLastTimeConnectedMs);
|
||||
dest.writeInt(mSystemDataSyncFlags);
|
||||
}
|
||||
|
||||
private AssociationInfo(@NonNull Parcel in) {
|
||||
@@ -356,6 +370,7 @@ public final class AssociationInfo implements Parcelable {
|
||||
mRevoked = in.readBoolean();
|
||||
mTimeApprovedMs = in.readLong();
|
||||
mLastTimeConnectedMs = in.readLong();
|
||||
mSystemDataSyncFlags = in.readInt();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -390,27 +405,24 @@ public final class AssociationInfo implements Parcelable {
|
||||
return new Builder(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
/** @hide */
|
||||
public static final class Builder implements NonActionableBuilder {
|
||||
@NonNull
|
||||
private final AssociationInfo mOriginalInfo;
|
||||
private boolean mNotifyOnDeviceNearby;
|
||||
private boolean mRevoked;
|
||||
private long mLastTimeConnectedMs;
|
||||
private int mSystemDataSyncFlags;
|
||||
|
||||
private Builder(@NonNull AssociationInfo info) {
|
||||
mOriginalInfo = info;
|
||||
mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
|
||||
mRevoked = info.mRevoked;
|
||||
mLastTimeConnectedMs = info.mLastTimeConnectedMs;
|
||||
mSystemDataSyncFlags = info.mSystemDataSyncFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be used by the CompanionDeviceManagerService.
|
||||
* @hide
|
||||
*/
|
||||
/** @hide */
|
||||
@Override
|
||||
@NonNull
|
||||
public Builder setLastTimeConnected(long lastTimeConnectedMs) {
|
||||
@@ -423,10 +435,7 @@ public final class AssociationInfo implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be used by the CompanionDeviceManagerService.
|
||||
* @hide
|
||||
*/
|
||||
/** @hide */
|
||||
@Override
|
||||
@NonNull
|
||||
public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
|
||||
@@ -434,10 +443,7 @@ public final class AssociationInfo implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be used by the CompanionDeviceManagerService.
|
||||
* @hide
|
||||
*/
|
||||
/** @hide */
|
||||
@Override
|
||||
@NonNull
|
||||
public Builder setRevoked(boolean revoked) {
|
||||
@@ -445,9 +451,15 @@ public final class AssociationInfo implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
/** @hide */
|
||||
@Override
|
||||
@NonNull
|
||||
public Builder setSystemDataSyncFlags(int flags) {
|
||||
mSystemDataSyncFlags = flags;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
public AssociationInfo build() {
|
||||
return new AssociationInfo(
|
||||
@@ -462,7 +474,8 @@ public final class AssociationInfo implements Parcelable {
|
||||
mNotifyOnDeviceNearby,
|
||||
mRevoked,
|
||||
mOriginalInfo.mTimeApprovedMs,
|
||||
mLastTimeConnectedMs
|
||||
mLastTimeConnectedMs,
|
||||
mSystemDataSyncFlags
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -480,25 +493,20 @@ public final class AssociationInfo implements Parcelable {
|
||||
* @hide
|
||||
*/
|
||||
public interface NonActionableBuilder {
|
||||
/**
|
||||
* Should only be used by the CompanionDeviceManagerService.
|
||||
* @hide
|
||||
*/
|
||||
/** @hide */
|
||||
@NonNull
|
||||
Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby);
|
||||
|
||||
/**
|
||||
* Should only be used by the CompanionDeviceManagerService.
|
||||
* @hide
|
||||
*/
|
||||
/** @hide */
|
||||
@NonNull
|
||||
Builder setLastTimeConnected(long lastTimeConnectedMs);
|
||||
|
||||
/**
|
||||
* Should only be used by the CompanionDeviceManagerService.
|
||||
* @hide
|
||||
*/
|
||||
/** @hide */
|
||||
@NonNull
|
||||
Builder setRevoked(boolean revoked);
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
Builder setSystemDataSyncFlags(int flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +166,19 @@ public final class CompanionDeviceManager {
|
||||
*/
|
||||
public static final String REASON_CANCELED = "canceled";
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
|
||||
FLAG_CALL_METADATA,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface DataSyncTypes {}
|
||||
|
||||
/**
|
||||
* Used by {@link #enableSystemDataSync(int, int)}}.
|
||||
* Sync call metadata like muting, ending and silencing a call.
|
||||
*
|
||||
*/
|
||||
public static final int FLAG_CALL_METADATA = 1;
|
||||
|
||||
/**
|
||||
* A device, returned in the activity result of the {@link IntentSender} received in
|
||||
@@ -468,6 +481,49 @@ public final class CompanionDeviceManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Enable system data sync (it only supports call metadata sync for now).
|
||||
* By default all supported system data types are enabled.</p>
|
||||
*
|
||||
* <p>Calling this API requires a uses-feature
|
||||
* {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
|
||||
*
|
||||
* @param associationId id of the device association.
|
||||
* @param flags system data types to be enabled.
|
||||
*/
|
||||
public void enableSystemDataSync(int associationId, @DataSyncTypes int flags) {
|
||||
if (!checkFeaturePresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mService.enableSystemDataSync(associationId, flags);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Disable system data sync (it only supports call metadata sync for now).
|
||||
* By default all supported system data types are enabled.</p>
|
||||
*
|
||||
* <p>Calling this API requires a uses-feature
|
||||
* {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
|
||||
*
|
||||
* @param associationId id of the device association.
|
||||
* @param flags system data types to be disabled.
|
||||
*/
|
||||
public void disableSystemDataSync(int associationId, @DataSyncTypes int flags) {
|
||||
if (!checkFeaturePresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mService.disableSystemDataSync(associationId, flags);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calling this API requires a uses-feature
|
||||
|
||||
@@ -84,4 +84,8 @@ interface ICompanionDeviceManager {
|
||||
boolean isCompanionApplicationBound(String packageName, int userId);
|
||||
|
||||
PendingIntent buildAssociationCancellationIntent(in String callingPackage, int userId);
|
||||
|
||||
void enableSystemDataSync(int associationId, int flags);
|
||||
|
||||
void disableSystemDataSync(int associationId, int flags);
|
||||
}
|
||||
|
||||
@@ -268,9 +268,9 @@ class AssociationRequestsProcessor {
|
||||
@NonNull ResultReceiver resultReceiver) {
|
||||
final long callingIdentity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
createAssociation(userId, packageName, macAddress,
|
||||
request.getDisplayName(), request.getDeviceProfile(),
|
||||
request.getAssociatedDevice(), request.isSelfManaged(),
|
||||
createAssociation(userId, packageName, macAddress, request.getDisplayName(),
|
||||
request.getDeviceProfile(), request.getAssociatedDevice(),
|
||||
request.isSelfManaged(),
|
||||
callback, resultReceiver);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingIdentity);
|
||||
@@ -287,7 +287,8 @@ class AssociationRequestsProcessor {
|
||||
|
||||
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
|
||||
macAddress, displayName, deviceProfile, associatedDevice, selfManaged,
|
||||
/* notifyOnDeviceNearby */ false, /* revoked */ false, timestamp, Long.MAX_VALUE);
|
||||
/* notifyOnDeviceNearby */ false, /* revoked */ false, timestamp, Long.MAX_VALUE,
|
||||
/* systemDataSyncFlags */ ~0);
|
||||
|
||||
if (deviceProfile != null) {
|
||||
// If the "Device Profile" is specified, make the companion application a holder of the
|
||||
@@ -315,6 +316,20 @@ class AssociationRequestsProcessor {
|
||||
// that there are other devices with the same profile, so the role holder won't be removed.
|
||||
}
|
||||
|
||||
public void enableSystemDataSync(int associationId, int flags) {
|
||||
AssociationInfo association = mAssociationStore.getAssociationById(associationId);
|
||||
AssociationInfo updated = AssociationInfo.builder(association)
|
||||
.setSystemDataSyncFlags(association.getSystemDataSyncFlags() | flags).build();
|
||||
mAssociationStore.updateAssociation(updated);
|
||||
}
|
||||
|
||||
public void disableSystemDataSync(int associationId, int flags) {
|
||||
AssociationInfo association = mAssociationStore.getAssociationById(associationId);
|
||||
AssociationInfo updated = AssociationInfo.builder(association)
|
||||
.setSystemDataSyncFlags(association.getSystemDataSyncFlags() & (~flags)).build();
|
||||
mAssociationStore.updateAssociation(updated);
|
||||
}
|
||||
|
||||
private void addAssociationToStore(@NonNull AssociationInfo association,
|
||||
@Nullable String deviceProfile) {
|
||||
Slog.i(TAG, "New CDM association created=" + association);
|
||||
|
||||
@@ -713,6 +713,18 @@ public class CompanionDeviceManagerService extends SystemService {
|
||||
mTransportManager.detachSystemDataTransport(packageName, userId, associationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableSystemDataSync(int associationId, int flags) {
|
||||
getAssociationWithCallerChecks(associationId);
|
||||
mAssociationRequestsProcessor.enableSystemDataSync(associationId, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableSystemDataSync(int associationId, int flags) {
|
||||
getAssociationWithCallerChecks(associationId);
|
||||
mAssociationRequestsProcessor.disableSystemDataSync(associationId, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDeviceAppeared(int associationId) {
|
||||
if (DEBUG) Log.i(TAG, "notifyDevice_Appeared() id=" + associationId);
|
||||
|
||||
@@ -132,7 +132,8 @@ import java.util.concurrent.ConcurrentMap;
|
||||
* notify_device_nearby="false"
|
||||
* revoked="false"
|
||||
* last_time_connected="1634641160229"
|
||||
* time_approved="1634389553216"/>
|
||||
* time_approved="1634389553216"
|
||||
* system_data_sync_flags="-1"/>
|
||||
*
|
||||
* <association
|
||||
* id="3"
|
||||
@@ -143,7 +144,8 @@ import java.util.concurrent.ConcurrentMap;
|
||||
* notify_device_nearby="false"
|
||||
* revoked="false"
|
||||
* last_time_connected="1634641160229"
|
||||
* time_approved="1634641160229"/>
|
||||
* time_approved="1634641160229"
|
||||
* system_data_sync_flags="-1"/>
|
||||
* </associations>
|
||||
*
|
||||
* <previously-used-ids>
|
||||
@@ -185,6 +187,7 @@ final class PersistentDataStore {
|
||||
private static final String XML_ATTR_REVOKED = "revoked";
|
||||
private static final String XML_ATTR_TIME_APPROVED = "time_approved";
|
||||
private static final String XML_ATTR_LAST_TIME_CONNECTED = "last_time_connected";
|
||||
private static final String XML_ATTR_SYSTEM_DATA_SYNC_FLAGS = "system_data_sync_flags";
|
||||
|
||||
private static final String LEGACY_XML_ATTR_DEVICE = "device";
|
||||
|
||||
@@ -429,7 +432,7 @@ final class PersistentDataStore {
|
||||
out.add(new AssociationInfo(associationId, userId, appPackage,
|
||||
MacAddress.fromString(deviceAddress), null, profile, null,
|
||||
/* managedByCompanionApp */ false, notify, /* revoked */ false, timeApproved,
|
||||
Long.MAX_VALUE));
|
||||
Long.MAX_VALUE, /* systemDataSyncFlags */ -1));
|
||||
}
|
||||
|
||||
private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
|
||||
@@ -462,10 +465,12 @@ final class PersistentDataStore {
|
||||
final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
|
||||
final long lastTimeConnected = readLongAttribute(
|
||||
parser, XML_ATTR_LAST_TIME_CONNECTED, Long.MAX_VALUE);
|
||||
final int systemDataSyncFlags = readIntAttribute(parser,
|
||||
XML_ATTR_SYSTEM_DATA_SYNC_FLAGS, -1);
|
||||
|
||||
final AssociationInfo associationInfo = createAssociationInfoNoThrow(associationId, userId,
|
||||
appPackage, macAddress, displayName, profile, selfManaged, notify, revoked,
|
||||
timeApproved, lastTimeConnected);
|
||||
timeApproved, lastTimeConnected, systemDataSyncFlags);
|
||||
if (associationInfo != null) {
|
||||
out.add(associationInfo);
|
||||
}
|
||||
@@ -523,6 +528,7 @@ final class PersistentDataStore {
|
||||
writeLongAttribute(serializer, XML_ATTR_TIME_APPROVED, a.getTimeApprovedMs());
|
||||
writeLongAttribute(
|
||||
serializer, XML_ATTR_LAST_TIME_CONNECTED, a.getLastTimeConnectedMs());
|
||||
writeIntAttribute(serializer, XML_ATTR_SYSTEM_DATA_SYNC_FLAGS, a.getSystemDataSyncFlags());
|
||||
|
||||
serializer.endTag(null, XML_TAG_ASSOCIATION);
|
||||
}
|
||||
@@ -561,14 +567,15 @@ final class PersistentDataStore {
|
||||
private static AssociationInfo createAssociationInfoNoThrow(int associationId,
|
||||
@UserIdInt int userId, @NonNull String appPackage, @Nullable MacAddress macAddress,
|
||||
@Nullable CharSequence displayName, @Nullable String profile, boolean selfManaged,
|
||||
boolean notify, boolean revoked, long timeApproved, long lastTimeConnected) {
|
||||
boolean notify, boolean revoked, long timeApproved, long lastTimeConnected,
|
||||
int systemDataSyncFlags) {
|
||||
AssociationInfo associationInfo = null;
|
||||
try {
|
||||
// We do not persist AssociatedDevice, which means that AssociationInfo retrieved from
|
||||
// datastore is not guaranteed to be identical to the one from initial association.
|
||||
associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress,
|
||||
displayName, profile, null, selfManaged, notify, revoked,
|
||||
timeApproved, lastTimeConnected);
|
||||
timeApproved, lastTimeConnected, systemDataSyncFlags);
|
||||
} catch (Exception e) {
|
||||
if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e);
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback);
|
||||
|
||||
mAssociationInfo = new AssociationInfo(/* associationId= */ 1, 0, null,
|
||||
MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false, 0, 0);
|
||||
MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false, 0, 0, -1);
|
||||
|
||||
mVdms = new VirtualDeviceManagerService(mContext);
|
||||
mLocalService = mVdms.getLocalServiceInstance();
|
||||
|
||||
Reference in New Issue
Block a user