Creates PeopleManager API isConversation

Change-Id: I075cd72363ba0ea0e84bb57fe017fce20e051862
Test: atest DataManagerTest
Bug: 180122164
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cec2f98..79c8035 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -197,6 +197,7 @@
     field public static final String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
     field public static final String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY";
     field public static final String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE";
+    field public static final String READ_PEOPLE_DATA = "android.permission.READ_PEOPLE_DATA";
     field public static final String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES";
     field public static final String READ_PRINT_SERVICE_RECOMMENDATIONS = "android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS";
     field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
@@ -1275,6 +1276,14 @@
 
 }
 
+package android.app.people {
+
+  public final class PeopleManager {
+    method @RequiresPermission(android.Manifest.permission.READ_PEOPLE_DATA) public boolean isConversation(@NonNull String, @NonNull String);
+  }
+
+}
+
 package android.app.prediction {
 
   public final class AppPredictionContext implements android.os.Parcelable {
diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl
index ebe9f60..d000f3b 100644
--- a/core/java/android/app/people/IPeopleManager.aidl
+++ b/core/java/android/app/people/IPeopleManager.aidl
@@ -49,6 +49,9 @@
     /** Removes all the recent conversations and uncaches their cached shortcuts. */
     void removeAllRecentConversations();
 
+    /** Returns whether the shortcutId is backed by a Conversation in People Service. */
+    boolean isConversation(in String packageName, int userId, in String shortcutId);
+
     /**
      * Returns the last interaction with the specified conversation. If the
      * conversation can't be found or no interactions have been recorded, returns 0L.
diff --git a/core/java/android/app/people/PeopleManager.java b/core/java/android/app/people/PeopleManager.java
index de7ba62..d348edb 100644
--- a/core/java/android/app/people/PeopleManager.java
+++ b/core/java/android/app/people/PeopleManager.java
@@ -17,6 +17,8 @@
 package android.app.people;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
@@ -53,6 +55,33 @@
                 Context.PEOPLE_SERVICE));
     }
 
+    /**
+     * Returns whether a shortcut has a conversation associated.
+     *
+     * <p>Requires android.permission.READ_PEOPLE_DATA permission.
+     *
+     * <p>This method may return different results for the same shortcut over time, as an app adopts
+     * conversation features or if a user hasn't communicated with the conversation associated to
+     * the shortcut in a while, so the result should not be stored and relied on indefinitely by
+     * clients.
+     *
+     * @param packageName name of the package the conversation is part of
+     * @param shortcutId the shortcut id backing the conversation
+     * @return whether the {@shortcutId} is backed by a Conversation.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PEOPLE_DATA)
+    public boolean isConversation(@NonNull String packageName, @NonNull String shortcutId) {
+        Preconditions.checkStringNotEmpty(packageName);
+        Preconditions.checkStringNotEmpty(shortcutId);
+        try {
+            return mService.isConversation(packageName, mContext.getUserId(), shortcutId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     /**
      * Sets or updates a {@link ConversationStatus} for a conversation.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6456e4e..e268fde 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5496,6 +5496,12 @@
     <permission android:name="android.permission.GET_PEOPLE_TILE_PREVIEW"
         android:protectionLevel="signature|recents" />
 
+    <!-- @hide @SystemApi Allows an application to retrieve whether shortcut is backed by a
+         Conversation.
+         TODO(b/180412052): STOPSHIP: Define a role so it can be granted to Shell and AiAi. -->
+    <permission android:name="android.permission.READ_PEOPLE_DATA"
+                android:protectionLevel="signature|appPredictor|recents"/>
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index fe4d0cf..8fd5d80 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -478,6 +478,8 @@
         <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" />
         <!-- Permission required for CTS test - CtsRebootReadinessTestCases -->
         <permission name="android.permission.SIGNAL_REBOOT_READINESS" />
+        <!-- Permission required for CTS test - PeopleManagerTest -->
+        <permission name="android.permission.READ_PEOPLE_DATA" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 539a81b..dbd25ce 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -406,6 +406,9 @@
     <!-- Permission required for CTS test - CtsRebootReadinessTestCases -->
     <uses-permission android:name="android.permission.SIGNAL_REBOOT_READINESS" />
 
+    <!-- Permission required for CTS test - PeopleManagerTest -->
+    <uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 5453de1..9666337 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.people;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -29,6 +30,7 @@
 import android.app.prediction.AppTargetEvent;
 import android.app.prediction.IPredictionCallback;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.os.Binder;
@@ -184,6 +186,20 @@
         }
 
         @Override
+        public boolean isConversation(String packageName, int userId, String shortcutId) {
+            enforceHasReadPeopleDataPermission();
+            handleIncomingUser(userId);
+            return mDataManager.isConversation(packageName, userId, shortcutId);
+        }
+
+        private void enforceHasReadPeopleDataPermission() throws SecurityException {
+            if (getContext().checkCallingPermission(Manifest.permission.READ_PEOPLE_DATA)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Caller doesn't have READ_PEOPLE_DATA permission.");
+            }
+        }
+
+        @Override
         public long getLastInteraction(String packageName, int userId, String shortcutId) {
             enforceSystemRootOrSystemUI(getContext(), "get last interaction");
             return mDataManager.getLastInteraction(packageName, userId, shortcutId);
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 444f9c6..1048dfd 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -58,6 +58,7 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.telecom.TelecomManager;
+import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -354,6 +355,14 @@
         });
     }
 
+    /** Returns whether {@code shortcutId} is backed by Conversation. */
+    public boolean isConversation(String packageName, int userId, String shortcutId) {
+        ConversationChannel channel = getConversation(packageName, userId, shortcutId);
+        return channel != null
+                && channel.getShortcutInfo() != null
+                && !TextUtils.isEmpty(channel.getShortcutInfo().getLabel());
+    }
+
     /**
      * Returns the last notification interaction with the specified conversation. If the
      * conversation can't be found or no interactions have been recorded, returns 0L.
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 6e57896..50d9f61 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -601,6 +601,22 @@
     }
 
     @Test
+    public void testIsConversation() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+        assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+                TEST_SHORTCUT_ID)).isFalse();
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        shortcut.setCached(ShortcutInfo.FLAG_PINNED);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+        assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+                TEST_SHORTCUT_ID)).isTrue();
+        assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+                TEST_SHORTCUT_ID + "1")).isFalse();
+    }
+
+    @Test
     public void testNotificationChannelCreated() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
         mDataManager.onUserUnlocked(USER_ID_SECONDARY);