Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a96767e authored by Christine Franks's avatar Christine Franks Committed by Automerger Merge Worker
Browse files

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

parents 3ff29df4 ad2e7289
Loading
Loading
Loading
Loading
+188 −0
Original line number Diff line number Diff line
/*
 * 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];
            }
        };
    }
}
+31 −13
Original line number Diff line number Diff line
@@ -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.
     *
+64 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}