1 /*
2  * Copyright (C) 2017 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.app;
18 
19 import android.annotation.SystemApi;
20 import android.app.Service;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.InstantAppResolveInfo;
24 import android.os.Build;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.IRemoteCallback;
29 import android.os.Looper;
30 import android.os.Message;
31 import android.os.RemoteException;
32 import android.util.Log;
33 import android.util.Slog;
34 
35 import com.android.internal.os.SomeArgs;
36 
37 import java.util.Arrays;
38 import java.util.List;
39 
40 /**
41  * Base class for implementing the resolver service.
42  * @hide
43  */
44 @SystemApi
45 public abstract class InstantAppResolverService extends Service {
46     private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
47     private static final String TAG = "PackageManager";
48 
49     /** @hide */
50     public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO";
51     /** @hide */
52     public static final String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE";
53     Handler mHandler;
54 
55     /**
56      * Called to retrieve resolve info for instant applications.
57      *
58      * @param digestPrefix The hash prefix of the instant app's domain.
59      */
onGetInstantAppResolveInfo( int digestPrefix[], String token, InstantAppResolutionCallback callback)60     public void onGetInstantAppResolveInfo(
61             int digestPrefix[], String token, InstantAppResolutionCallback callback) {
62         throw new IllegalStateException("Must define");
63     }
64 
65     /**
66      * Called to retrieve intent filters for instant applications.
67      *
68      * @param digestPrefix The hash prefix of the instant app's domain.
69      */
onGetInstantAppIntentFilter( int digestPrefix[], String token, InstantAppResolutionCallback callback)70     public void onGetInstantAppIntentFilter(
71             int digestPrefix[], String token, InstantAppResolutionCallback callback) {
72         throw new IllegalStateException("Must define");
73     }
74 
75     /**
76      * Returns a {@link Looper} to perform service operations on.
77      */
getLooper()78     Looper getLooper() {
79         return getBaseContext().getMainLooper();
80     }
81 
82     @Override
attachBaseContext(Context base)83     public final void attachBaseContext(Context base) {
84         super.attachBaseContext(base);
85         mHandler = new ServiceHandler(getLooper());
86     }
87 
88     @Override
onBind(Intent intent)89     public final IBinder onBind(Intent intent) {
90         return new IInstantAppResolver.Stub() {
91             @Override
92             public void getInstantAppResolveInfoList(
93                     int digestPrefix[], String token, int sequence, IRemoteCallback callback) {
94                 if (DEBUG_EPHEMERAL) {
95                     Slog.v(TAG, "[" + token + "] Phase1 called; posting");
96                 }
97                 final SomeArgs args = SomeArgs.obtain();
98                 args.arg1 = callback;
99                 args.arg2 = digestPrefix;
100                 args.arg3 = token;
101                 mHandler.obtainMessage(
102                                 ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence, 0, args)
103                         .sendToTarget();
104             }
105 
106             @Override
107             public void getInstantAppIntentFilterList(
108                     int digestPrefix[], String token, String hostName, IRemoteCallback callback) {
109                 if (DEBUG_EPHEMERAL) {
110                     Slog.v(TAG, "[" + token + "] Phase2 called; posting");
111                 }
112                 final SomeArgs args = SomeArgs.obtain();
113                 args.arg1 = callback;
114                 args.arg2 = digestPrefix;
115                 args.arg3 = token;
116                 args.arg4 = hostName;
117                 mHandler.obtainMessage(
118                         ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, callback).sendToTarget();
119             }
120         };
121     }
122 
123     /**
124      * Callback to post results from instant app resolution.
125      */
126     public static final class InstantAppResolutionCallback {
127         private final IRemoteCallback mCallback;
128         private final int mSequence;
129         InstantAppResolutionCallback(int sequence, IRemoteCallback callback) {
130             mCallback = callback;
131             mSequence = sequence;
132         }
133 
134         public void onInstantAppResolveInfo(List<InstantAppResolveInfo> resolveInfo) {
135             final Bundle data = new Bundle();
136             data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo);
137             data.putInt(EXTRA_SEQUENCE, mSequence);
138             try {
139                 mCallback.sendResult(data);
140             } catch (RemoteException e) {
141             }
142         }
143     }
144 
145     @Deprecated
146     void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
147             InstantAppResolutionCallback callback) {
148         if (DEBUG_EPHEMERAL) {
149             Slog.d(TAG, "[" + token + "] Phase1 request;"
150                     + " prefix: " + Arrays.toString(digestPrefix));
151         }
152         onGetInstantAppResolveInfo(digestPrefix, token, callback);
153     }
154     @Deprecated
155     void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName,
156             InstantAppResolutionCallback callback) {
157         if (DEBUG_EPHEMERAL) {
158             Slog.d(TAG, "[" + token + "] Phase2 request;"
159                     + " prefix: " + Arrays.toString(digestPrefix));
160         }
161         onGetInstantAppIntentFilter(digestPrefix, token, callback);
162     }
163 
164     private final class ServiceHandler extends Handler {
165         public static final int MSG_GET_INSTANT_APP_RESOLVE_INFO = 1;
166         public static final int MSG_GET_INSTANT_APP_INTENT_FILTER = 2;
167 
168         public ServiceHandler(Looper looper) {
169             super(looper, null /*callback*/, true /*async*/);
170         }
171 
172         @Override
173         @SuppressWarnings("unchecked")
174         public void handleMessage(Message message) {
175             final int action = message.what;
176             switch (action) {
177                 case MSG_GET_INSTANT_APP_RESOLVE_INFO: {
178                     final SomeArgs args = (SomeArgs) message.obj;
179                     final IRemoteCallback callback = (IRemoteCallback) args.arg1;
180                     final int[] digestPrefix = (int[]) args.arg2;
181                     final String token = (String) args.arg3;
182                     final int sequence = message.arg1;
183                     _onGetInstantAppResolveInfo(
184                             digestPrefix, token,
185                             new InstantAppResolutionCallback(sequence, callback));
186                 } break;
187 
188                 case MSG_GET_INSTANT_APP_INTENT_FILTER: {
189                     final SomeArgs args = (SomeArgs) message.obj;
190                     final IRemoteCallback callback = (IRemoteCallback) args.arg1;
191                     final int[] digestPrefix = (int[]) args.arg2;
192                     final String token = (String) args.arg3;
193                     final String hostName = (String) args.arg4;
194                     _onGetInstantAppIntentFilter(
195                             digestPrefix, token, hostName,
196                             new InstantAppResolutionCallback(-1 /*sequence*/, callback));
197                 } break;
198 
199                 default: {
200                     throw new IllegalArgumentException("Unknown message: " + action);
201                 }
202             }
203         }
204     }
205 }
206