• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.rotary;
18 
19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
20 
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.content.pm.ResolveInfo;
24 import android.text.TextUtils;
25 import android.view.accessibility.AccessibilityNodeInfo;
26 
27 import androidx.annotation.NonNull;
28 import androidx.annotation.Nullable;
29 import androidx.annotation.VisibleForTesting;
30 
31 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
32 import com.android.internal.util.dump.DualDumpOutputStream;
33 
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Set;
37 
38 /**
39  * A helper class to support apps using {@link android.view.SurfaceView} for off-process rendering.
40  * <p>
41  * There are two kinds of apps involved in the off-process rendering process: the client apps and
42  * the host app. A client app holds a {@link android.view.SurfaceView} and delegates its rendering
43  * process to the host app. The host app uses the data provided by the client app to render the app
44  * content in the surface provided by the SurfaceView.
45  * <p>
46  * Although both the client app and the host app have independent <strong>view</strong> hierarchies,
47  * their <strong>node</strong> hierarchies are connected. The node hierarchy of the host app is
48  * embedded into the node hierarchy of the client app. To be more specific, the root node of the
49  * host app is the only child of the SurfaceView node, which is a leaf node of the client app.
50  */
51 class SurfaceViewHelper {
52 
53     /** The intent action to be used by the host app to bind to the RendererService. */
54     private static final String RENDER_ACTION = "android.car.template.host.RendererService";
55 
56     /** Package names of the client apps. */
57     private final Set<CharSequence> mClientApps = new HashSet<>();
58 
59     /** Package name of the host app. */
60     @Nullable
61     @VisibleForTesting
62     String mHostApp;
63 
64     /** Initializes the package name of the host app. */
initHostApp(@onNull PackageManager packageManager)65     void initHostApp(@NonNull PackageManager packageManager) {
66         List<ResolveInfo> rendererServices = packageManager.queryIntentServices(
67                 new Intent(RENDER_ACTION), PackageManager.GET_RESOLVED_FILTER);
68         if (rendererServices == null || rendererServices.isEmpty()) {
69             L.v("No host app found");
70             return;
71         }
72         mHostApp = rendererServices.get(0).serviceInfo.packageName;
73         L.v("Host app has been initialized: " + mHostApp);
74     }
75 
76     /** Clears the package name of the host app if the given {@code packageName} matches. */
clearHostApp(@onNull String packageName)77     void clearHostApp(@NonNull String packageName) {
78         if (packageName.equals(mHostApp)) {
79             mHostApp = null;
80             L.v("Host app has been set to null");
81         }
82     }
83 
84     /** Adds the package name of the client app. */
addClientApp(@onNull CharSequence clientAppPackageName)85     void addClientApp(@NonNull CharSequence clientAppPackageName) {
86         mClientApps.add(clientAppPackageName);
87     }
88 
89     /** Returns whether the given {@code node} represents a view of the host app. */
isHostNode(@onNull AccessibilityNodeInfo node)90     boolean isHostNode(@NonNull AccessibilityNodeInfo node) {
91         return !TextUtils.isEmpty(mHostApp) && mHostApp.equals(node.getPackageName());
92     }
93 
94     /** Returns whether the given {@code node} represents a view of the client app. */
isClientNode(@onNull AccessibilityNodeInfo node)95     boolean isClientNode(@NonNull AccessibilityNodeInfo node) {
96         return mClientApps.contains(node.getPackageName());
97     }
98 
99     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(@onNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto, @NonNull String fieldName, long fieldId)100     void dump(@NonNull DualDumpOutputStream dumpOutputStream, boolean dumpAsProto,
101             @NonNull String fieldName, long fieldId) {
102         long fieldToken = dumpOutputStream.start(fieldName, fieldId);
103         dumpOutputStream.write("hostApp", RotaryProtos.SurfaceViewHelper.HOST_APP, mHostApp);
104         DumpUtils.writeCharSequences(dumpOutputStream, dumpAsProto, "clientApps",
105                 RotaryProtos.SurfaceViewHelper.CLIENT_APPS, mClientApps);
106         dumpOutputStream.end(fieldToken);
107     }
108 
109 }
110