diff --git a/core/java/android/app/people/ConversationChannel.java b/core/java/android/app/people/ConversationChannel.java new file mode 100644 index 0000000000000..39c5c85e456c7 --- /dev/null +++ b/core/java/android/app/people/ConversationChannel.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 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 android.app.people; + +import android.app.NotificationChannel; +import android.content.pm.ShortcutInfo; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The non-customized notification channel of a conversation. It contains the information to render + * the conversation and allows the user to open and customize the conversation setting. + * + * @hide + */ +public final class ConversationChannel implements Parcelable { + + private ShortcutInfo mShortcutInfo; + private NotificationChannel mParentNotificationChannel; + private long mLastEventTimestamp; + private boolean mHasActiveNotifications; + + public static final Creator CREATOR = new Creator() { + @Override + public ConversationChannel createFromParcel(Parcel in) { + return new ConversationChannel(in); + } + + @Override + public ConversationChannel[] newArray(int size) { + return new ConversationChannel[size]; + } + }; + + public ConversationChannel(ShortcutInfo shortcutInfo, + NotificationChannel parentNotificationChannel, long lastEventTimestamp, + boolean hasActiveNotifications) { + mShortcutInfo = shortcutInfo; + mParentNotificationChannel = parentNotificationChannel; + mLastEventTimestamp = lastEventTimestamp; + mHasActiveNotifications = hasActiveNotifications; + } + + public ConversationChannel(Parcel in) { + mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader()); + mParentNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader()); + mLastEventTimestamp = in.readLong(); + mHasActiveNotifications = in.readBoolean(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mShortcutInfo, flags); + dest.writeParcelable(mParentNotificationChannel, flags); + dest.writeLong(mLastEventTimestamp); + dest.writeBoolean(mHasActiveNotifications); + } + + public ShortcutInfo getShortcutInfo() { + return mShortcutInfo; + } + + public NotificationChannel getParentNotificationChannel() { + return mParentNotificationChannel; + } + + public long getLastEventTimestamp() { + return mLastEventTimestamp; + } + + /** + * Whether this conversation has any active notifications. If it's true, the shortcut for this + * conversation can't be uncached until all its active notifications are dismissed. + */ + public boolean hasActiveNotifications() { + return mHasActiveNotifications; + } +} diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl new file mode 100644 index 0000000000000..61dac0d64422d --- /dev/null +++ b/core/java/android/app/people/IPeopleManager.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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 android.app.people; + +import android.content.pm.ParceledListSlice; +import android.net.Uri; +import android.os.IBinder; + +/** + * System private API for talking with the people service. + * {@hide} + */ +interface IPeopleManager { + /** + * Returns the recent conversations. The conversations that have customized notification + * settings are excluded from the returned list. + */ + ParceledListSlice getRecentConversations(); + + /** + * Removes the specified conversation from the recent conversations list and uncaches the + * shortcut associated with the conversation. + */ + void removeRecentConversation(in String packageName, int userId, in String shortcutId); + + /** Removes all the recent conversations and uncaches their cached shortcuts. */ + void removeAllRecentConversations(); +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 16cdf2334ad84..52b04675b7a50 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3496,6 +3496,7 @@ public abstract class Context { //@hide: TIME_ZONE_DETECTOR_SERVICE, PERMISSION_SERVICE, LIGHTS_SERVICE, + //@hide: PEOPLE_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -5188,6 +5189,14 @@ public abstract class Context { */ public static final String SMS_SERVICE = "sms"; + /** + * Use with {@link #getSystemService(String)} to access people service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String PEOPLE_SERVICE = "people"; + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 37bf664918826..33317a38853e9 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -19,6 +19,8 @@ package com.android.server.people; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.people.ConversationChannel; +import android.app.people.IPeopleManager; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionSessionId; import android.app.prediction.AppTarget; @@ -26,8 +28,11 @@ import android.app.prediction.AppTargetEvent; import android.app.prediction.IPredictionCallback; import android.content.Context; import android.content.pm.ParceledListSlice; +import android.os.Binder; import android.os.CancellationSignal; +import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.Slog; @@ -35,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemService; import com.android.server.people.data.DataManager; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -68,6 +74,7 @@ public class PeopleService extends SystemService { @Override public void onStart() { + publishBinderService(Context.PEOPLE_SERVICE, new BinderService()); publishLocalService(PeopleServiceInternal.class, new LocalService()); } @@ -81,6 +88,38 @@ public class PeopleService extends SystemService { mDataManager.onUserStopping(user.getUserIdentifier()); } + /** + * Enforces that only the system or root UID can make certain calls. + * + * @param message used as message if SecurityException is thrown + * @throws SecurityException if the caller is not system or root + */ + private static void enforceSystemOrRoot(String message) { + int uid = Binder.getCallingUid(); + if (!UserHandle.isSameApp(uid, Process.SYSTEM_UID) && uid != Process.ROOT_UID) { + throw new SecurityException("Only system may " + message); + } + } + + private final class BinderService extends IPeopleManager.Stub { + + @Override + public ParceledListSlice getRecentConversations() { + enforceSystemOrRoot("get recent conversations"); + return new ParceledListSlice<>(new ArrayList<>()); + } + + @Override + public void removeRecentConversation(String packageName, int userId, String shortcutId) { + enforceSystemOrRoot("remove a recent conversation"); + } + + @Override + public void removeAllRecentConversations() { + enforceSystemOrRoot("remove all recent conversations"); + } + } + @VisibleForTesting final class LocalService extends PeopleServiceInternal {