blob: 23173a820a2ee801834f02b3924f23110dcdbc96 [file] [log] [blame]
Sean Pontd00aefb2020-01-07 12:05:09 -08001/*
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
17package android.service.quickaccesswallet;
18
19import android.Manifest;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.content.ComponentName;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.pm.PackageManager;
27import android.content.pm.ResolveInfo;
28import android.content.pm.ServiceInfo;
29import android.content.res.Resources;
30import android.content.res.TypedArray;
31import android.content.res.XmlResourceParser;
32import android.provider.Settings;
33import android.util.AttributeSet;
34import android.util.Log;
35import android.util.Xml;
36
37import com.android.internal.R;
38
39import org.xmlpull.v1.XmlPullParser;
40import org.xmlpull.v1.XmlPullParserException;
41
42import java.io.IOException;
43import java.util.List;
44
45/**
46 * {@link ServiceInfo} and meta-data about a {@link QuickAccessWalletService}.
47 *
48 * @hide
49 */
50class QuickAccessWalletServiceInfo {
51
52 private static final String TAG = "QAWalletSInfo";
53 private static final String TAG_WALLET_SERVICE = "quickaccesswallet-service";
54
55 private final ServiceInfo mServiceInfo;
56 private final ServiceMetadata mServiceMetadata;
57
58 private QuickAccessWalletServiceInfo(
59 @NonNull ServiceInfo serviceInfo,
60 @NonNull ServiceMetadata metadata) {
61 mServiceInfo = serviceInfo;
62 mServiceMetadata = metadata;
63 }
64
65 @Nullable
66 static QuickAccessWalletServiceInfo tryCreate(@NonNull Context context) {
67 ComponentName defaultPaymentApp = getDefaultPaymentApp(context);
68 if (defaultPaymentApp == null) {
69 Log.d(TAG, "create: default payment app not set");
70 return null;
71 }
72
73 ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultPaymentApp.getPackageName());
74 if (serviceInfo == null) {
75 Log.d(TAG, "create: unable to resolve service intent");
76 return null;
77 }
78
79 if (!Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE.equals(serviceInfo.permission)) {
Sean Pont72fc25f2020-02-11 19:02:44 -080080 Log.w(TAG, String.format("%s.%s does not require permission %s",
81 serviceInfo.packageName, serviceInfo.name,
82 Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE));
Sean Pontd00aefb2020-01-07 12:05:09 -080083 return null;
84 }
85
86 ServiceMetadata metadata = parseServiceMetadata(context, serviceInfo);
87 return new QuickAccessWalletServiceInfo(serviceInfo, metadata);
88 }
89
90 private static ComponentName getDefaultPaymentApp(Context context) {
91 ContentResolver cr = context.getContentResolver();
92 String comp = Settings.Secure.getString(cr, Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
93 return comp == null ? null : ComponentName.unflattenFromString(comp);
94 }
95
96 private static ServiceInfo getWalletServiceInfo(Context context, String packageName) {
97 Intent intent = new Intent(QuickAccessWalletService.SERVICE_INTERFACE);
98 intent.setPackage(packageName);
99 List<ResolveInfo> resolveInfos =
100 context.getPackageManager().queryIntentServices(intent,
101 PackageManager.MATCH_DEFAULT_ONLY);
102 return resolveInfos.isEmpty() ? null : resolveInfos.get(0).serviceInfo;
103 }
104
105 private static class ServiceMetadata {
106 @Nullable
107 private final String mSettingsActivity;
108 @Nullable
109 private final String mWalletActivity;
110
111 private ServiceMetadata(String settingsActivity, String walletActivity) {
112 this.mSettingsActivity = settingsActivity;
113 this.mWalletActivity = walletActivity;
114 }
115 }
116
117 private static ServiceMetadata parseServiceMetadata(Context context, ServiceInfo serviceInfo) {
118 PackageManager pm = context.getPackageManager();
119 final XmlResourceParser parser =
120 serviceInfo.loadXmlMetaData(pm, QuickAccessWalletService.SERVICE_META_DATA);
121
122 if (parser == null) {
123 return new ServiceMetadata(null, null);
124 }
125
126 try {
127 Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
128 int type = 0;
129 while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
130 type = parser.next();
131 }
132
133 if (TAG_WALLET_SERVICE.equals(parser.getName())) {
134 final AttributeSet allAttributes = Xml.asAttributeSet(parser);
135 TypedArray afsAttributes = null;
136 try {
137 afsAttributes = resources.obtainAttributes(allAttributes,
138 R.styleable.QuickAccessWalletService);
139 String settingsActivity = afsAttributes.getString(
140 R.styleable.QuickAccessWalletService_settingsActivity);
141 String walletActivity = afsAttributes.getString(
142 R.styleable.QuickAccessWalletService_targetActivity);
143 return new ServiceMetadata(settingsActivity, walletActivity);
144 } finally {
145 if (afsAttributes != null) {
146 afsAttributes.recycle();
147 }
148 }
149 } else {
150 Log.e(TAG, "Meta-data does not start with quickaccesswallet-service tag");
151 }
152
153 } catch (PackageManager.NameNotFoundException
154 | IOException
155 | XmlPullParserException e) {
156 Log.e(TAG, "Error parsing quickaccesswallet service meta-data", e);
157 }
158 return new ServiceMetadata(null, null);
159 }
160
161 /**
162 * @return the component name of the {@link QuickAccessWalletService}
163 */
164 @NonNull
165 ComponentName getComponentName() {
166 return mServiceInfo.getComponentName();
167 }
168
169 /**
170 * @return the fully qualified name of the activity that hosts the full wallet. If available,
171 * this intent should be started with the action
172 * {@link QuickAccessWalletService#ACTION_VIEW_WALLET}
173 */
174 @Nullable
175 String getWalletActivity() {
176 return mServiceMetadata.mWalletActivity;
177 }
178
179 /**
180 * @return the fully qualified name of the activity that allows the user to change quick access
181 * wallet settings. If available, this intent should be started with the action {@link
182 * QuickAccessWalletService#ACTION_VIEW_WALLET_SETTINGS}
183 */
184 @Nullable
185 String getSettingsActivity() {
186 return mServiceMetadata.mSettingsActivity;
187 }
188}