• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.tv.settings.basic;
18 
19 import static java.nio.charset.StandardCharsets.UTF_8;
20 
21 import android.accounts.AccountManager;
22 import android.app.Activity;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.ResolveInfo;
28 import android.net.Uri;
29 import android.os.UserManager;
30 import android.text.TextUtils;
31 import android.util.Log;
32 
33 import androidx.annotation.NonNull;
34 
35 import com.android.tv.settings.util.ResourcesUtil;
36 
37 import java.io.InputStream;
38 import java.io.InputStreamReader;
39 import java.util.List;
40 import java.util.Optional;
41 
42 import org.xmlpull.v1.XmlPullParser;
43 import org.xmlpull.v1.XmlPullParserFactory;
44 
45 /**
46  * Implementation of {@link BasicModeFeatureProvider}.
47  */
48 public class BasicModeFeatureProviderImplX implements BasicModeFeatureProvider {
49 
50     private static final String TAG = "BasicModeFeatureX";
51     private static final String OEM_AUTHORITY = "tvlauncher.config";
52     private static final String OEM_CONTRACT_SCHEME = "content";
53     private static final String OEM_CONFIG_PATH = "configuration";
54     private static final String DEVICE_MODE = "device-mode";
55     private static final String STORE_DEMO = "store_demo";
56     private static final String VALUE = "value";
57 
isBasicMode(@onNull Context context)58     public boolean isBasicMode(@NonNull Context context) {
59         if (!context.getSystemService(UserManager.class).isSystemUser()) {
60             return false;
61         }
62         int numAccounts = AccountManager.get(context).getAccounts().length;
63         Log.i(TAG, "Number of accounts: " + numAccounts);
64         return numAccounts == 0;
65     }
66 
67     @Override
startBasicModeExitActivity(@onNull Activity activity)68     public void startBasicModeExitActivity(@NonNull Activity activity) {
69         final String basicModeExit = ResourcesUtil.getString(activity, "basic_mode_exit_data");
70         startBasicModeExitActivity(activity, basicModeExit);
71     }
72 
73     @Override
startBasicModeInternetBlock(@onNull Activity activity)74     public void startBasicModeInternetBlock(@NonNull Activity activity) {
75         final String basicModeExit = ResourcesUtil.getString(activity, "basic_mode_exit_internet");
76         startBasicModeExitActivity(activity, basicModeExit);
77     }
78 
startBasicModeExitActivity(@onNull Activity activity, String basicModeExitType)79     private void startBasicModeExitActivity(@NonNull Activity activity, String basicModeExitType) {
80         final String basicModeExitPackage = ResourcesUtil.getString(activity,
81                 "basic_mode_exit_package");
82         final String basicModeExitComponent =
83                 ResourcesUtil.getString(activity, "basic_mode_exit_component");
84         if (TextUtils.isEmpty(basicModeExitPackage) || TextUtils.isEmpty(basicModeExitComponent)
85                 || TextUtils.isEmpty(basicModeExitType)) {
86             Log.e(TAG, "Basic mode exit activity undefined.");
87             return;
88         }
89         ComponentName componentName =
90                 new ComponentName(basicModeExitPackage, basicModeExitComponent);
91         Uri dataUri = Uri.parse(basicModeExitType);
92         Intent intent = new Intent().setComponent(componentName).setData(dataUri);
93         List<ResolveInfo> intentHandlingActivities =
94                 activity.getPackageManager().queryIntentActivities(intent, 0);
95         for (ResolveInfo info : intentHandlingActivities) {
96             if (info.activityInfo != null && info.activityInfo.enabled) {
97                 Log.d(TAG, "Starting basic mode exit activity.");
98                 activity.startActivity(intent);
99                 if (!activity.isFinishing()) {
100                     // We finish TvSettings instead of leaving it dangling in the activity stack
101                     // as the expected Activity for handling basic mode exit is a HOME that also
102                     // intercepts BACK key pressing.
103                     activity.finish();
104                 }
105             }
106         }
107         Log.e(TAG, "Basic mode exit activity not found.");
108     }
109 
110     @Override
isStoreDemoMode(@onNull Context context)111     public boolean isStoreDemoMode(@NonNull Context context) {
112         if (getCustomizationAppPackageName(context).isEmpty()) {
113             // There is no customizations apk for this device.
114             return false;
115         }
116         Uri oemUri =
117                 new Uri.Builder()
118                         .scheme(OEM_CONTRACT_SCHEME)
119                         .authority(OEM_AUTHORITY)
120                         .path(OEM_CONFIG_PATH)
121                         .build();
122         try (InputStream inputStream =
123                      context.getContentResolver().openInputStream(oemUri)) {
124             if (inputStream == null) {
125                 return false;
126             }
127             XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
128             parser.setInput(new InputStreamReader(inputStream, UTF_8));
129             int eventType = parser.getEventType();
130             while (eventType != XmlPullParser.END_DOCUMENT) {
131                 if (eventType == XmlPullParser.START_TAG) {
132                     if (DEVICE_MODE.equals(parser.getName())) {
133                         Log.e(TAG, "Got is Store Demo: " + STORE_DEMO.equals(
134                                 parser.getAttributeValue(null, VALUE)));
135                         return STORE_DEMO.equals(parser.getAttributeValue(null, VALUE));
136                     }
137                 }
138                 eventType = parser.next();
139             }
140             return false;
141         } catch (Exception e) {
142             // Fallback to default mode if we can't get a value.
143             Log.e(TAG, "Unable to determine store demo mode", e);
144             return false;
145         }
146     }
147 
getCustomizationAppPackageName(@onNull Context context)148     private Optional<String> getCustomizationAppPackageName(@NonNull Context context) {
149         return Optional.ofNullable(
150                         context
151                                 .getPackageManager()
152                                 .resolveContentProvider(
153                                         OEM_AUTHORITY, /* flags= */ 0))
154                 .filter(providerInfo -> hasSystemAppFlags(providerInfo.applicationInfo))
155                 .map(providerInfo -> providerInfo.applicationInfo.packageName);
156     }
157 
hasSystemAppFlags(ApplicationInfo info)158     private static boolean hasSystemAppFlags(ApplicationInfo info) {
159         return (info.flags & (ApplicationInfo.FLAG_UPDATED_SYSTEM_APP
160                 | ApplicationInfo.FLAG_SYSTEM))
161                 != 0;
162     }
163 }
164