Merge "Add and update cross device sync data holders" into udc-dev

This commit is contained in:
Christine Franks
2023-04-20 06:51:43 +00:00
committed by Android (Google) Code Review
3 changed files with 283 additions and 13 deletions

View File

@@ -0,0 +1,188 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.companion.datatransfer.contextsync;
import android.annotation.NonNull;
import android.companion.ContextSyncMessage;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/** A read-only snapshot of an {@link ContextSyncMessage}. */
class CallMetadataSyncData {
final Map<Long, CallMetadataSyncData.Call> mCalls = new HashMap<>();
final List<CallMetadataSyncData.Call> mRequests = new ArrayList<>();
public void addCall(CallMetadataSyncData.Call call) {
mCalls.put(call.getId(), call);
}
public boolean hasCall(long id) {
return mCalls.containsKey(id);
}
public Collection<CallMetadataSyncData.Call> getCalls() {
return mCalls.values();
}
public void addRequest(CallMetadataSyncData.Call call) {
mRequests.add(call);
}
public List<CallMetadataSyncData.Call> getRequests() {
return mRequests;
}
public static class Call implements Parcelable {
private long mId;
private String mCallerId;
private byte[] mAppIcon;
private String mAppName;
private String mAppIdentifier;
private int mStatus;
private final Set<Integer> mControls = new HashSet<>();
public static Call fromParcel(Parcel parcel) {
final Call call = new Call();
call.setId(parcel.readLong());
call.setCallerId(parcel.readString());
call.setAppIcon(parcel.readBlob());
call.setAppName(parcel.readString());
call.setAppIdentifier(parcel.readString());
call.setStatus(parcel.readInt());
final int numberOfControls = parcel.readInt();
for (int i = 0; i < numberOfControls; i++) {
call.addControl(parcel.readInt());
}
return call;
}
@Override
public void writeToParcel(Parcel parcel, int parcelableFlags) {
parcel.writeLong(mId);
parcel.writeString(mCallerId);
parcel.writeBlob(mAppIcon);
parcel.writeString(mAppName);
parcel.writeString(mAppIdentifier);
parcel.writeInt(mStatus);
parcel.writeInt(mControls.size());
for (int control : mControls) {
parcel.writeInt(control);
}
}
void setId(long id) {
mId = id;
}
void setCallerId(String callerId) {
mCallerId = callerId;
}
void setAppIcon(byte[] appIcon) {
mAppIcon = appIcon;
}
void setAppName(String appName) {
mAppName = appName;
}
void setAppIdentifier(String appIdentifier) {
mAppIdentifier = appIdentifier;
}
void setStatus(int status) {
mStatus = status;
}
void addControl(int control) {
mControls.add(control);
}
long getId() {
return mId;
}
String getCallerId() {
return mCallerId;
}
byte[] getAppIcon() {
return mAppIcon;
}
String getAppName() {
return mAppName;
}
String getAppIdentifier() {
return mAppIdentifier;
}
int getStatus() {
return mStatus;
}
Set<Integer> getControls() {
return mControls;
}
boolean hasControl(int control) {
return mControls.contains(control);
}
@Override
public boolean equals(Object other) {
if (other instanceof CallMetadataSyncData.Call) {
return ((Call) other).getId() == getId();
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(mId);
}
@Override
public int describeContents() {
return 0;
}
@NonNull public static final Parcelable.Creator<Call> CREATOR = new Parcelable.Creator<>() {
@Override
public Call createFromParcel(Parcel source) {
return Call.fromParcel(source);
}
@Override
public Call[] newArray(int size) {
return new Call[size];
}
};
}
}

View File

@@ -39,6 +39,8 @@ public class CrossDeviceCall {
private static final String TAG = "CrossDeviceCall";
public static final String EXTRA_CALL_ID =
"com.android.companion.datatransfer.contextsync.extra.CALL_ID";
private static final int APP_ICON_BITMAP_DIMENSION = 256;
private static final AtomicLong sNextId = new AtomicLong(1);
@@ -47,6 +49,7 @@ public class CrossDeviceCall {
private final Call mCall;
@VisibleForTesting boolean mIsEnterprise;
@VisibleForTesting boolean mIsOtt;
private final String mCallingAppPackageName;
private String mCallingAppName;
private byte[] mCallingAppIcon;
private String mCallerDisplayName;
@@ -59,7 +62,7 @@ public class CrossDeviceCall {
CallAudioState callAudioState) {
mId = sNextId.getAndIncrement();
mCall = call;
final String callingAppPackageName = call != null
mCallingAppPackageName = call != null
? call.getDetails().getAccountHandle().getComponentName().getPackageName() : null;
mIsOtt = call != null
&& (call.getDetails().getCallCapabilities() & Call.Details.PROPERTY_SELF_MANAGED)
@@ -69,13 +72,13 @@ public class CrossDeviceCall {
== Call.Details.PROPERTY_ENTERPRISE_CALL;
try {
final ApplicationInfo applicationInfo = packageManager
.getApplicationInfo(callingAppPackageName,
.getApplicationInfo(mCallingAppPackageName,
PackageManager.ApplicationInfoFlags.of(0));
mCallingAppName = packageManager.getApplicationLabel(applicationInfo).toString();
mCallingAppIcon = renderDrawableToByteArray(
packageManager.getApplicationIcon(applicationInfo));
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Could not get application info for package " + callingAppPackageName, e);
Slog.e(TAG, "Could not get application info for package " + mCallingAppPackageName, e);
}
mIsMuted = callAudioState != null && callAudioState.isMuted();
if (call != null) {
@@ -170,7 +173,8 @@ public class CrossDeviceCall {
}
}
private int convertStateToStatus(int callState) {
/** Converts a Telecom call state to a Context Sync status. */
public static int convertStateToStatus(int callState) {
switch (callState) {
case Call.STATE_HOLDING:
return android.companion.Telecom.Call.ON_HOLD;
@@ -178,20 +182,30 @@ public class CrossDeviceCall {
return android.companion.Telecom.Call.ONGOING;
case Call.STATE_RINGING:
return android.companion.Telecom.Call.RINGING;
case Call.STATE_NEW:
case Call.STATE_DIALING:
case Call.STATE_DISCONNECTED:
case Call.STATE_SELECT_PHONE_ACCOUNT:
case Call.STATE_CONNECTING:
case Call.STATE_DISCONNECTING:
case Call.STATE_PULLING_CALL:
case Call.STATE_AUDIO_PROCESSING:
case Call.STATE_SIMULATED_RINGING:
default:
return android.companion.Telecom.Call.UNKNOWN_STATUS;
}
}
/**
* Converts a Context Sync status to a Telecom call state. Note that this is lossy for
* and RINGING_SILENCED, as Telecom does not distinguish between RINGING and RINGING_SILENCED.
*/
public static int convertStatusToState(int status) {
switch (status) {
case android.companion.Telecom.Call.ON_HOLD:
return Call.STATE_HOLDING;
case android.companion.Telecom.Call.ONGOING:
return Call.STATE_ACTIVE;
case android.companion.Telecom.Call.RINGING:
case android.companion.Telecom.Call.RINGING_SILENCED:
return Call.STATE_RINGING;
case android.companion.Telecom.Call.UNKNOWN_STATUS:
default:
return Call.STATE_NEW;
}
}
public long getId() {
return mId;
}
@@ -208,6 +222,10 @@ public class CrossDeviceCall {
return mCallingAppIcon;
}
public String getCallingAppPackageName() {
return mCallingAppPackageName;
}
/**
* Get a human-readable "caller id" to display as the origin of the call.
*

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.companion.datatransfer.contextsync;
import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
import android.testing.AndroidTestingRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
public class CallMetadataSyncDataTest {
@Test
public void call_writeToParcel_fromParcel_reconstructsSuccessfully() {
final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
final long id = 5;
final String callerId = "callerId";
final byte[] appIcon = "appIcon".getBytes();
final String appName = "appName";
final String appIdentifier = "com.google.test";
final int status = 1;
final int control1 = 2;
final int control2 = 3;
call.setId(id);
call.setCallerId(callerId);
call.setAppIcon(appIcon);
call.setAppName(appName);
call.setAppIdentifier(appIdentifier);
call.setStatus(status);
call.addControl(control1);
call.addControl(control2);
Parcel parcel = Parcel.obtain();
call.writeToParcel(parcel, /* flags= */ 0);
parcel.setDataPosition(0);
final CallMetadataSyncData.Call reconstructedCall = CallMetadataSyncData.Call.fromParcel(
parcel);
assertThat(reconstructedCall.getId()).isEqualTo(id);
assertThat(reconstructedCall.getCallerId()).isEqualTo(callerId);
assertThat(reconstructedCall.getAppIcon()).isEqualTo(appIcon);
assertThat(reconstructedCall.getAppName()).isEqualTo(appName);
assertThat(reconstructedCall.getAppIdentifier()).isEqualTo(appIdentifier);
assertThat(reconstructedCall.getStatus()).isEqualTo(status);
assertThat(reconstructedCall.getControls()).containsExactly(control1, control2);
}
}