Loading core/java/android/service/dreams/DreamService.java +153 −4 Original line number Diff line number Diff line Loading @@ -31,6 +31,11 @@ import android.content.ComponentName; 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; Loading @@ -39,9 +44,11 @@ import android.os.Looper; 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; Loading @@ -57,9 +64,14 @@ import android.view.WindowManager; 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; Loading Loading @@ -159,8 +171,9 @@ import java.util.function.Consumer; * </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. Loading Loading @@ -190,6 +203,11 @@ public class DreamService extends Service implements Window.Callback { */ 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 Loading Loading @@ -239,13 +257,16 @@ public class DreamService extends Service implements Window.Callback { 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); Loading Loading @@ -967,7 +988,8 @@ public class DreamService extends Service implements Window.Callback { // 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; Loading Loading @@ -1080,6 +1102,86 @@ public class DreamService extends Service implements Window.Callback { // 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. * Loading Loading @@ -1242,6 +1344,30 @@ public class DreamService extends Service implements Window.Callback { 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); Loading Loading @@ -1302,4 +1428,27 @@ public class DreamService extends Service implements Window.Callback { onWindowCreated(a.getWindow()); } } /** * Represents metadata defined in {@link android.R.styleable#Dream <dream>}. * * @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; } } } packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java +7 −85 Original line number Diff line number Diff line Loading @@ -26,8 +26,6 @@ import android.content.pm.PackageManager; 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; Loading @@ -35,21 +33,14 @@ import android.provider.Settings; 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; Loading Loading @@ -185,15 +176,18 @@ public class DreamBackend { 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; } Loading Loading @@ -483,78 +477,6 @@ public class DreamBackend { 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)); Loading services/tests/servicestests/AndroidManifest.xml +13 −0 Original line number Diff line number Diff line Loading @@ -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"> Loading services/tests/servicestests/res/xml/test_dream_metadata.xml 0 → 100644 +19 −0 Original line number Diff line number Diff line <!-- ~ 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" /> services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java 0 → 100644 +55 −0 Original line number Diff line number Diff line /* * 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); } } Loading
core/java/android/service/dreams/DreamService.java +153 −4 Original line number Diff line number Diff line Loading @@ -31,6 +31,11 @@ import android.content.ComponentName; 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; Loading @@ -39,9 +44,11 @@ import android.os.Looper; 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; Loading @@ -57,9 +64,14 @@ import android.view.WindowManager; 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; Loading Loading @@ -159,8 +171,9 @@ import java.util.function.Consumer; * </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. Loading Loading @@ -190,6 +203,11 @@ public class DreamService extends Service implements Window.Callback { */ 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 Loading Loading @@ -239,13 +257,16 @@ public class DreamService extends Service implements Window.Callback { 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); Loading Loading @@ -967,7 +988,8 @@ public class DreamService extends Service implements Window.Callback { // 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; Loading Loading @@ -1080,6 +1102,86 @@ public class DreamService extends Service implements Window.Callback { // 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. * Loading Loading @@ -1242,6 +1344,30 @@ public class DreamService extends Service implements Window.Callback { 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); Loading Loading @@ -1302,4 +1428,27 @@ public class DreamService extends Service implements Window.Callback { onWindowCreated(a.getWindow()); } } /** * Represents metadata defined in {@link android.R.styleable#Dream <dream>}. * * @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; } } }
packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java +7 −85 Original line number Diff line number Diff line Loading @@ -26,8 +26,6 @@ import android.content.pm.PackageManager; 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; Loading @@ -35,21 +33,14 @@ import android.provider.Settings; 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; Loading Loading @@ -185,15 +176,18 @@ public class DreamBackend { 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; } Loading Loading @@ -483,78 +477,6 @@ public class DreamBackend { 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)); Loading
services/tests/servicestests/AndroidManifest.xml +13 −0 Original line number Diff line number Diff line Loading @@ -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"> Loading
services/tests/servicestests/res/xml/test_dream_metadata.xml 0 → 100644 +19 −0 Original line number Diff line number Diff line <!-- ~ 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" />
services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java 0 → 100644 +55 −0 Original line number Diff line number Diff line /* * 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); } }