Merge changes from topic "revert-16781464-YCPFURVHZR"

* changes:
  (Resubmit) Read "showClockAndComplications" metadata.
  (Resubmit) Move dream metadata parsing to framework.
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index bb1f393..3459172 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -31,6 +31,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -39,9 +44,11 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.util.Xml;
 import android.view.ActionMode;
 import android.view.Display;
 import android.view.KeyEvent;
@@ -57,9 +64,14 @@
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 
+import com.android.internal.R;
 import com.android.internal.util.DumpUtils;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.function.Consumer;
@@ -159,8 +171,9 @@
  * </pre>
  */
 public class DreamService extends Service implements Window.Callback {
-    private final String mTag =
-            DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]";
+    private static final String TAG = DreamService.class.getSimpleName();
+    private final String mTag = TAG + "[" + getClass().getSimpleName() + "]";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     /**
      * The name of the dream manager service.
@@ -191,6 +204,11 @@
     public static final String DREAM_META_DATA = "android.service.dream";
 
     /**
+     * Name of the root tag under which a Dream defines its metadata in an XML file.
+     */
+    private static final String DREAM_META_DATA_ROOT_TAG = "dream";
+
+    /**
      * Extra containing a boolean for whether to show complications on the overlay.
      * @hide
      */
@@ -239,13 +257,16 @@
             mRequests = new ArrayDeque<>();
         }
 
-        public void bind(Context context, @Nullable ComponentName overlayService) {
+        public void bind(Context context, @Nullable ComponentName overlayService,
+                ComponentName dreamService) {
             if (overlayService == null) {
                 return;
             }
 
             final Intent overlayIntent = new Intent();
             overlayIntent.setComponent(overlayService);
+            overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS,
+                    fetchShouldShowComplications(context, dreamService));
 
             context.bindService(overlayIntent,
                     this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
@@ -967,7 +988,8 @@
 
         // Connect to the overlay service if present.
         if (!mWindowless) {
-            mOverlayConnection.bind(this, intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT));
+            mOverlayConnection.bind(this, intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT),
+                    new ComponentName(this, getClass()));
         }
 
         return mDreamServiceWrapper;
@@ -1081,6 +1103,86 @@
     // end public api
 
     /**
+     * Parses and returns metadata of the dream service indicated by the service info. Returns null
+     * if metadata cannot be found.
+     *
+     * Note that {@link ServiceInfo} must be fetched with {@link PackageManager#GET_META_DATA} flag.
+     *
+     * @hide
+     */
+    @Nullable
+    public static DreamMetadata getDreamMetadata(Context context, ServiceInfo serviceInfo) {
+        final PackageManager pm = context.getPackageManager();
+
+        final TypedArray rawMetadata = readMetadata(pm, serviceInfo);
+        if (rawMetadata == null) return null;
+
+        final DreamMetadata metadata = new DreamMetadata(
+                convertToComponentName(rawMetadata.getString(
+                        com.android.internal.R.styleable.Dream_settingsActivity), serviceInfo),
+                rawMetadata.getDrawable(
+                        com.android.internal.R.styleable.Dream_previewImage),
+                rawMetadata.getBoolean(R.styleable.Dream_showClockAndComplications,
+                        DEFAULT_SHOW_COMPLICATIONS));
+        rawMetadata.recycle();
+        return metadata;
+    }
+
+    /**
+     * Returns the raw XML metadata fetched from the {@link ServiceInfo}.
+     *
+     * Returns <code>null</code> if the {@link ServiceInfo} doesn't contain valid dream metadata.
+     */
+    @Nullable
+    private static TypedArray readMetadata(PackageManager pm, ServiceInfo serviceInfo) {
+        if (serviceInfo == null || serviceInfo.metaData == null) {
+            return null;
+        }
+
+        try (XmlResourceParser parser =
+                     serviceInfo.loadXmlMetaData(pm, DreamService.DREAM_META_DATA)) {
+            if (parser == null) {
+                if (DEBUG) Log.w(TAG, "No " + DreamService.DREAM_META_DATA + " metadata");
+                return null;
+            }
+
+            final AttributeSet attrs = Xml.asAttributeSet(parser);
+            while (true) {
+                final int type = parser.next();
+                if (type == XmlPullParser.END_DOCUMENT || type == XmlPullParser.START_TAG) {
+                    break;
+                }
+            }
+
+            if (!parser.getName().equals(DREAM_META_DATA_ROOT_TAG)) {
+                if (DEBUG) {
+                    Log.w(TAG, "Metadata does not start with " + DREAM_META_DATA_ROOT_TAG + " tag");
+                }
+                return null;
+            }
+
+            return pm.getResourcesForApplication(serviceInfo.applicationInfo).obtainAttributes(
+                    attrs, com.android.internal.R.styleable.Dream);
+        } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
+            if (DEBUG) Log.e(TAG, "Error parsing: " + serviceInfo.packageName, e);
+            return null;
+        }
+    }
+
+    private static ComponentName convertToComponentName(String flattenedString,
+            ServiceInfo serviceInfo) {
+        if (flattenedString == null) {
+            return null;
+        }
+
+        if (!flattenedString.contains("/")) {
+            return new ComponentName(serviceInfo.packageName, flattenedString);
+        }
+
+        return ComponentName.unflattenFromString(flattenedString);
+    }
+
+    /**
      * Called by DreamController.stopDream() when the Dream is about to be unbound and destroyed.
      *
      * Must run on mHandler.
@@ -1242,6 +1344,30 @@
         return (oldFlags&~mask) | (flags&mask);
     }
 
+    /**
+     * Fetches metadata of the dream indicated by the {@link ComponentName}, and returns whether
+     * the dream should show complications on the overlay. If not defined, returns
+     * {@link DreamService#DEFAULT_SHOW_COMPLICATIONS}.
+     */
+    private static boolean fetchShouldShowComplications(Context context,
+            ComponentName componentName) {
+        final PackageManager pm = context.getPackageManager();
+
+        try {
+            final ServiceInfo si = pm.getServiceInfo(componentName,
+                    PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
+            final DreamMetadata metadata = getDreamMetadata(context, si);
+
+            if (metadata != null) {
+                return metadata.showComplications;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            if (DEBUG) Log.w(TAG, "cannot find component " + componentName.flattenToShortString());
+        }
+
+        return DEFAULT_SHOW_COMPLICATIONS;
+    }
+
     @Override
     protected void dump(final FileDescriptor fd, PrintWriter pw, final String[] args) {
         DumpUtils.dumpAsync(mHandler, (pw1, prefix) -> dumpOnHandler(fd, pw1, args), pw, "", 1000);
@@ -1302,4 +1428,27 @@
             onWindowCreated(a.getWindow());
         }
     }
+
+    /**
+     * Represents metadata defined in {@link android.R.styleable#Dream &lt;dream&gt;}.
+     *
+     * @hide
+     */
+    public static final class DreamMetadata {
+        @Nullable
+        public final ComponentName settingsActivity;
+
+        @Nullable
+        public final Drawable previewImage;
+
+        @NonNull
+        public final boolean showComplications;
+
+        DreamMetadata(ComponentName settingsActivity, Drawable previewImage,
+                boolean showComplications) {
+            this.settingsActivity = settingsActivity;
+            this.previewImage = previewImage;
+            this.showComplications = showComplications;
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index d179b82..0102a97 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -26,8 +26,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -35,21 +33,14 @@
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 import android.text.TextUtils;
-import android.util.AttributeSet;
 import android.util.Log;
-import android.util.Xml;
 
 import com.android.settingslib.R;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
@@ -185,15 +176,18 @@
             dreamInfo.componentName = componentName;
             dreamInfo.isActive = dreamInfo.componentName.equals(activeDream);
 
-            final DreamMetadata dreamMetadata = getDreamMetadata(pm, resolveInfo);
-            dreamInfo.settingsComponentName = dreamMetadata.mSettingsActivity;
-            dreamInfo.previewImage = dreamMetadata.mPreviewImage;
+            final DreamService.DreamMetadata dreamMetadata = DreamService.getDreamMetadata(mContext,
+                    resolveInfo.serviceInfo);
+            if (dreamMetadata != null) {
+                dreamInfo.settingsComponentName = dreamMetadata.settingsActivity;
+                dreamInfo.previewImage = dreamMetadata.previewImage;
+            }
             if (dreamInfo.previewImage == null) {
                 dreamInfo.previewImage = mDreamPreviewDefault;
             }
             dreamInfos.add(dreamInfo);
         }
-        Collections.sort(dreamInfos, mComparator);
+        dreamInfos.sort(mComparator);
         return dreamInfos;
     }
 
@@ -483,78 +477,6 @@
         return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
     }
 
-    private static final class DreamMetadata {
-        @Nullable
-        Drawable mPreviewImage;
-        @Nullable
-        ComponentName mSettingsActivity;
-    }
-
-    @Nullable
-    private static TypedArray readMetadata(PackageManager pm, ServiceInfo serviceInfo) {
-        if (serviceInfo == null || serviceInfo.metaData == null) {
-            return null;
-        }
-        try (XmlResourceParser parser =
-                     serviceInfo.loadXmlMetaData(pm, DreamService.DREAM_META_DATA)) {
-            if (parser == null) {
-                Log.w(TAG, "No " + DreamService.DREAM_META_DATA + " meta-data");
-                return null;
-            }
-            Resources res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
-            AttributeSet attrs = Xml.asAttributeSet(parser);
-            while (true) {
-                final int type = parser.next();
-                if (type == XmlPullParser.END_DOCUMENT || type == XmlPullParser.START_TAG) {
-                    break;
-                }
-            }
-            String nodeName = parser.getName();
-            if (!"dream".equals(nodeName)) {
-                Log.w(TAG, "Meta-data does not start with dream tag");
-                return null;
-            }
-            return res.obtainAttributes(attrs, com.android.internal.R.styleable.Dream);
-        } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
-            Log.w(TAG, "Error parsing : " + serviceInfo.packageName, e);
-            return null;
-        }
-    }
-
-    private static ComponentName convertToComponentName(String flattenedString,
-            ServiceInfo serviceInfo) {
-        if (flattenedString == null) return null;
-
-        if (flattenedString.indexOf('/') < 0) {
-            flattenedString = serviceInfo.packageName + "/" + flattenedString;
-        }
-
-        ComponentName cn = ComponentName.unflattenFromString(flattenedString);
-
-        if (cn == null) return null;
-        if (!cn.getPackageName().equals(serviceInfo.packageName)) {
-            Log.w(TAG,
-                    "Inconsistent package name in component: " + cn.getPackageName()
-                            + ", should be: " + serviceInfo.packageName);
-            return null;
-        }
-
-        return cn;
-    }
-
-    private static DreamMetadata getDreamMetadata(PackageManager pm, ResolveInfo resolveInfo) {
-        DreamMetadata result = new DreamMetadata();
-        if (resolveInfo == null) return result;
-        TypedArray rawMetadata = readMetadata(pm, resolveInfo.serviceInfo);
-        if (rawMetadata == null) return result;
-        result.mSettingsActivity = convertToComponentName(rawMetadata.getString(
-                com.android.internal.R.styleable.Dream_settingsActivity), resolveInfo.serviceInfo);
-        result.mPreviewImage = rawMetadata.getDrawable(
-                com.android.internal.R.styleable.Dream_previewImage);
-        rawMetadata.recycle();
-        return result;
-    }
-
     private static void logd(String msg, Object... args) {
         if (DEBUG) {
             Log.d(TAG, args == null || args.length == 0 ? msg : String.format(msg, args));
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index d9f73d9..53cab9e 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -130,6 +130,19 @@
                android:resource="@xml/test_account_type2_authenticator"/>
         </service>
 
+        <service
+            android:name="com.android.server.dreams.TestDreamService"
+            android:exported="false"
+            android:label="Test Dream" >
+            <intent-filter>
+                <action android:name="android.service.dreams.DreamService" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data
+                android:name="android.service.dream"
+                android:resource="@xml/test_dream_metadata" />
+        </service>
+
         <receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
              android:permission="android.permission.BIND_DEVICE_ADMIN"
              android:exported="true">
diff --git a/services/tests/servicestests/res/xml/test_dream_metadata.xml b/services/tests/servicestests/res/xml/test_dream_metadata.xml
new file mode 100644
index 0000000..aa054f1
--- /dev/null
+++ b/services/tests/servicestests/res/xml/test_dream_metadata.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<dream xmlns:android="http://schemas.android.com/apk/res/android"
+       android:settingsActivity="com.android.server.dreams/.TestDreamSettingsActivity"
+       android:showClockAndComplications="false" />
diff --git a/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java b/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java
new file mode 100644
index 0000000..74d2e0f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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.dreams;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.service.dreams.DreamService;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DreamServiceTest {
+    @Test
+    public void testMetadataParsing() throws PackageManager.NameNotFoundException {
+        final String testPackageName = "com.android.frameworks.servicestests";
+        final String testDreamClassName = "com.android.server.dreams.TestDreamService";
+        final String testSettingsActivity = "com.android.server.dreams/.TestDreamSettingsActivity";
+
+        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        final ServiceInfo si = context.getPackageManager().getServiceInfo(
+                new ComponentName(testPackageName, testDreamClassName),
+                PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
+        final DreamService.DreamMetadata metadata = DreamService.getDreamMetadata(context, si);
+
+        assertEquals(0, metadata.settingsActivity.compareTo(
+                ComponentName.unflattenFromString(testSettingsActivity)));
+        assertFalse(metadata.showComplications);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/dreams/TestDreamService.java b/services/tests/servicestests/src/com/android/server/dreams/TestDreamService.java
new file mode 100644
index 0000000..3c99a98
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/dreams/TestDreamService.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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.dreams;
+
+import android.service.dreams.DreamService;
+
+/**
+ * Dream service implementation for unit testing.
+ */
+public class TestDreamService extends DreamService {
+}