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