1 /* 2 * Copyright (C) 2020 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 android.service.quickaccesswallet; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.ComponentName; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.PackageManager; 27 import android.content.pm.ResolveInfo; 28 import android.content.pm.ServiceInfo; 29 import android.content.res.Resources; 30 import android.content.res.TypedArray; 31 import android.content.res.XmlResourceParser; 32 import android.graphics.drawable.Drawable; 33 import android.provider.Settings; 34 import android.text.TextUtils; 35 import android.util.AttributeSet; 36 import android.util.Log; 37 import android.util.Xml; 38 39 import com.android.internal.R; 40 41 import org.xmlpull.v1.XmlPullParser; 42 import org.xmlpull.v1.XmlPullParserException; 43 44 import java.io.IOException; 45 import java.util.List; 46 47 /** 48 * {@link ServiceInfo} and meta-data about a {@link QuickAccessWalletService}. 49 * 50 * @hide 51 */ 52 class QuickAccessWalletServiceInfo { 53 54 private static final String TAG = "QAWalletSInfo"; 55 private static final String TAG_WALLET_SERVICE = "quickaccesswallet-service"; 56 57 private final ServiceInfo mServiceInfo; 58 private final ServiceMetadata mServiceMetadata; 59 private final TileServiceMetadata mTileServiceMetadata; 60 QuickAccessWalletServiceInfo( @onNull ServiceInfo serviceInfo, @NonNull ServiceMetadata metadata, @NonNull TileServiceMetadata tileServiceMetadata)61 private QuickAccessWalletServiceInfo( 62 @NonNull ServiceInfo serviceInfo, 63 @NonNull ServiceMetadata metadata, 64 @NonNull TileServiceMetadata tileServiceMetadata) { 65 mServiceInfo = serviceInfo; 66 mServiceMetadata = metadata; 67 mTileServiceMetadata = tileServiceMetadata; 68 } 69 70 @Nullable tryCreate(@onNull Context context)71 static QuickAccessWalletServiceInfo tryCreate(@NonNull Context context) { 72 ComponentName defaultPaymentApp = getDefaultPaymentApp(context); 73 if (defaultPaymentApp == null) { 74 return null; 75 } 76 77 ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultPaymentApp.getPackageName()); 78 if (serviceInfo == null) { 79 return null; 80 } 81 82 if (!Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE.equals(serviceInfo.permission)) { 83 Log.w(TAG, String.format("%s.%s does not require permission %s", 84 serviceInfo.packageName, serviceInfo.name, 85 Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE)); 86 return null; 87 } 88 89 ServiceMetadata metadata = parseServiceMetadata(context, serviceInfo); 90 TileServiceMetadata tileServiceMetadata = 91 new TileServiceMetadata(parseTileServiceMetadata(context, serviceInfo)); 92 return new QuickAccessWalletServiceInfo(serviceInfo, metadata, tileServiceMetadata); 93 } 94 getDefaultPaymentApp(Context context)95 private static ComponentName getDefaultPaymentApp(Context context) { 96 ContentResolver cr = context.getContentResolver(); 97 String comp = Settings.Secure.getString(cr, Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT); 98 return comp == null ? null : ComponentName.unflattenFromString(comp); 99 } 100 getWalletServiceInfo(Context context, String packageName)101 private static ServiceInfo getWalletServiceInfo(Context context, String packageName) { 102 Intent intent = new Intent(QuickAccessWalletService.SERVICE_INTERFACE); 103 intent.setPackage(packageName); 104 List<ResolveInfo> resolveInfos = 105 context.getPackageManager().queryIntentServices(intent, 106 PackageManager.MATCH_DIRECT_BOOT_AWARE 107 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 108 | PackageManager.MATCH_DEFAULT_ONLY 109 | PackageManager.GET_META_DATA); 110 return resolveInfos.isEmpty() ? null : resolveInfos.get(0).serviceInfo; 111 } 112 113 private static class TileServiceMetadata { 114 @Nullable 115 private final Drawable mTileIcon; 116 TileServiceMetadata(@ullable Drawable tileIcon)117 private TileServiceMetadata(@Nullable Drawable tileIcon) { 118 mTileIcon = tileIcon; 119 } 120 } 121 122 @Nullable parseTileServiceMetadata(Context context, ServiceInfo serviceInfo)123 private static Drawable parseTileServiceMetadata(Context context, ServiceInfo serviceInfo) { 124 PackageManager pm = context.getPackageManager(); 125 int tileIconDrawableId = 126 serviceInfo.metaData.getInt(QuickAccessWalletService.TILE_SERVICE_META_DATA); 127 if (tileIconDrawableId != 0) { 128 try { 129 Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo); 130 return resources.getDrawable(tileIconDrawableId, null); 131 } catch (PackageManager.NameNotFoundException e) { 132 Log.e(TAG, "Error parsing quickaccesswallet tile service meta-data", e); 133 } 134 } 135 return null; 136 } 137 138 static class ServiceMetadata { 139 @Nullable 140 private final String mSettingsActivity; 141 @Nullable 142 private final String mTargetActivity; 143 @Nullable 144 private final CharSequence mShortcutShortLabel; 145 @Nullable 146 private final CharSequence mShortcutLongLabel; 147 empty()148 private static ServiceMetadata empty() { 149 return new ServiceMetadata(null, null, null, null); 150 } 151 ServiceMetadata( String targetActivity, String settingsActivity, CharSequence shortcutShortLabel, CharSequence shortcutLongLabel)152 private ServiceMetadata( 153 String targetActivity, 154 String settingsActivity, 155 CharSequence shortcutShortLabel, 156 CharSequence shortcutLongLabel) { 157 mTargetActivity = targetActivity; 158 mSettingsActivity = settingsActivity; 159 mShortcutShortLabel = shortcutShortLabel; 160 mShortcutLongLabel = shortcutLongLabel; 161 } 162 } 163 parseServiceMetadata(Context context, ServiceInfo serviceInfo)164 static ServiceMetadata parseServiceMetadata(Context context, ServiceInfo serviceInfo) { 165 PackageManager pm = context.getPackageManager(); 166 final XmlResourceParser parser = 167 serviceInfo.loadXmlMetaData(pm, QuickAccessWalletService.SERVICE_META_DATA); 168 169 if (parser == null) { 170 return ServiceMetadata.empty(); 171 } 172 173 try { 174 Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo); 175 int type = 0; 176 while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { 177 type = parser.next(); 178 } 179 180 if (TAG_WALLET_SERVICE.equals(parser.getName())) { 181 final AttributeSet allAttributes = Xml.asAttributeSet(parser); 182 TypedArray afsAttributes = null; 183 try { 184 afsAttributes = resources.obtainAttributes(allAttributes, 185 R.styleable.QuickAccessWalletService); 186 String targetActivity = afsAttributes.getString( 187 R.styleable.QuickAccessWalletService_targetActivity); 188 String settingsActivity = afsAttributes.getString( 189 R.styleable.QuickAccessWalletService_settingsActivity); 190 CharSequence shortcutShortLabel = afsAttributes.getText( 191 R.styleable.QuickAccessWalletService_shortcutShortLabel); 192 CharSequence shortcutLongLabel = afsAttributes.getText( 193 R.styleable.QuickAccessWalletService_shortcutLongLabel); 194 return new ServiceMetadata(targetActivity, settingsActivity, shortcutShortLabel, 195 shortcutLongLabel); 196 } finally { 197 if (afsAttributes != null) { 198 afsAttributes.recycle(); 199 } 200 } 201 } else { 202 Log.e(TAG, "Meta-data does not start with quickaccesswallet-service tag"); 203 } 204 } catch (PackageManager.NameNotFoundException 205 | IOException 206 | XmlPullParserException e) { 207 Log.e(TAG, "Error parsing quickaccesswallet service meta-data", e); 208 } 209 return ServiceMetadata.empty(); 210 } 211 212 /** 213 * @return the component name of the {@link QuickAccessWalletService} 214 */ 215 @NonNull getComponentName()216 ComponentName getComponentName() { 217 return mServiceInfo.getComponentName(); 218 } 219 220 /** 221 * @return the fully qualified name of the activity that hosts the full wallet. If available, 222 * this intent should be started with the action 223 * {@link QuickAccessWalletService#ACTION_VIEW_WALLET} 224 */ 225 @Nullable getWalletActivity()226 String getWalletActivity() { 227 return mServiceMetadata.mTargetActivity; 228 } 229 230 /** 231 * @return the fully qualified name of the activity that allows the user to change quick access 232 * wallet settings. If available, this intent should be started with the action {@link 233 * QuickAccessWalletService#ACTION_VIEW_WALLET_SETTINGS} 234 */ 235 @Nullable getSettingsActivity()236 String getSettingsActivity() { 237 return mServiceMetadata.mSettingsActivity; 238 } 239 240 @NonNull getWalletLogo(Context context)241 Drawable getWalletLogo(Context context) { 242 Drawable drawable = mServiceInfo.loadLogo(context.getPackageManager()); 243 if (drawable != null) { 244 return drawable; 245 } 246 return mServiceInfo.loadIcon(context.getPackageManager()); 247 } 248 249 @Nullable getTileIcon()250 Drawable getTileIcon() { 251 return mTileServiceMetadata.mTileIcon; 252 } 253 254 @NonNull getShortcutShortLabel(Context context)255 CharSequence getShortcutShortLabel(Context context) { 256 if (!TextUtils.isEmpty(mServiceMetadata.mShortcutShortLabel)) { 257 return mServiceMetadata.mShortcutShortLabel; 258 } 259 return mServiceInfo.loadLabel(context.getPackageManager()); 260 } 261 262 @NonNull getShortcutLongLabel(Context context)263 CharSequence getShortcutLongLabel(Context context) { 264 if (!TextUtils.isEmpty(mServiceMetadata.mShortcutLongLabel)) { 265 return mServiceMetadata.mShortcutLongLabel; 266 } 267 return mServiceInfo.loadLabel(context.getPackageManager()); 268 } 269 270 @NonNull getServiceLabel(Context context)271 CharSequence getServiceLabel(Context context) { 272 return mServiceInfo.loadLabel(context.getPackageManager()); 273 } 274 } 275