1 /*
2  * Copyright (C) 2019 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.stubs.am;
18 
19 import static com.android.frameworks.perftests.am.util.Constants.COMMAND_ACQUIRE_CONTENT_PROVIDER;
20 import static com.android.frameworks.perftests.am.util.Constants.COMMAND_BIND_SERVICE;
21 import static com.android.frameworks.perftests.am.util.Constants.COMMAND_RELEASE_CONTENT_PROVIDER;
22 import static com.android.frameworks.perftests.am.util.Constants.COMMAND_SEND_BROADCAST;
23 import static com.android.frameworks.perftests.am.util.Constants.COMMAND_START_ACTIVITY;
24 import static com.android.frameworks.perftests.am.util.Constants.COMMAND_STOP_ACTIVITY;
25 import static com.android.frameworks.perftests.am.util.Constants.COMMAND_UNBIND_SERVICE;
26 
27 import android.app.Service;
28 import android.content.BroadcastReceiver;
29 import android.content.ComponentName;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.IContentProvider;
33 import android.content.Intent;
34 import android.content.ServiceConnection;
35 import android.net.Uri;
36 import android.os.Binder;
37 import android.os.Bundle;
38 import android.os.Handler;
39 import android.os.HandlerThread;
40 import android.os.IBinder;
41 import android.os.Looper;
42 import android.os.Message;
43 import android.os.Messenger;
44 import android.os.RemoteException;
45 import android.util.ArrayMap;
46 import android.util.Log;
47 
48 import com.android.frameworks.perftests.am.util.Constants;
49 import com.android.frameworks.perftests.am.util.ICommandReceiver;
50 
51 public class InitService extends Service {
52     private static final String TAG = "InitService";
53     public static final boolean VERBOSE = false;
54 
55     private static class Stub extends ICommandReceiver.Stub {
56         private final Context mContext;
57         private final Messenger mCallback;
58         private final Handler mHandler;
59         private final Messenger mMessenger;
60         final ArrayMap<String, MyServiceConnection> mServices =
61                 new ArrayMap<String, MyServiceConnection>();
62         final ArrayMap<Uri, IContentProvider> mProviders =
63                 new ArrayMap<Uri, IContentProvider>();
64 
Stub(Context context, Messenger callback)65         Stub(Context context, Messenger callback) {
66             mContext = context;
67             mCallback = callback;
68             HandlerThread thread = new HandlerThread("result handler");
69             thread.start();
70             mHandler = new H(thread.getLooper());
71             mMessenger = new Messenger(mHandler);
72         }
73 
74         private class H extends Handler {
H(Looper looper)75             H(Looper looper) {
76                 super(looper);
77             }
78 
79             @Override
handleMessage(Message msg)80             public void handleMessage(Message msg) {
81                 if (msg.what == Constants.MSG_DEFAULT) {
82                     if (VERBOSE) {
83                         Log.i(TAG, "H: received seq=" + msg.arg1 + ", result=" + msg.arg2);
84                     }
85                     sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, msg.arg1, msg.arg2, null);
86                 } else if (msg.what == Constants.MSG_UNBIND_DONE) {
87                     if (VERBOSE) {
88                         Log.i(TAG, "H: received unbind=" + msg.obj);
89                     }
90                     synchronized (InitService.sStub) {
91                         Bundle b = (Bundle) msg.obj;
92                         String pkg = b.getString(Constants.EXTRA_SOURCE_PACKAGE, "");
93                         MyServiceConnection c = mServices.remove(pkg);
94                         if (c != null) {
95                             sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, c.mSeq,
96                                     Constants.RESULT_NO_ERROR, null);
97                         }
98                     }
99                 }
100             }
101         }
102 
103         @Override
sendCommand(int command, int seq, String sourcePackage, String targetPackage, int flags, Bundle bundle)104         public void sendCommand(int command, int seq, String sourcePackage, String targetPackage,
105                 int flags, Bundle bundle) {
106             if (VERBOSE) {
107                 Log.i(TAG, "Received command=" + command + ", seq=" + seq + ", from="
108                         + sourcePackage + ", to=" + targetPackage + ", flags=" + flags);
109             }
110             switch (command) {
111                 case COMMAND_BIND_SERVICE:
112                     handleBindService(seq, targetPackage, flags, bundle);
113                     break;
114                 case COMMAND_UNBIND_SERVICE:
115                     handleUnbindService(seq, targetPackage);
116                     break;
117                 case COMMAND_ACQUIRE_CONTENT_PROVIDER:
118                     acquireProvider(seq, bundle.getParcelable(Constants.EXTRA_URI));
119                     break;
120                 case COMMAND_RELEASE_CONTENT_PROVIDER:
121                     releaseProvider(seq, bundle.getParcelable(Constants.EXTRA_URI));
122                     break;
123                 case COMMAND_SEND_BROADCAST:
124                     sendBroadcast(seq, targetPackage);
125                     break;
126                 case COMMAND_START_ACTIVITY:
127                     startActivity(seq, targetPackage);
128                     break;
129                 case COMMAND_STOP_ACTIVITY:
130                     stopActivity(seq, targetPackage);
131                     break;
132             }
133         }
134 
handleBindService(int seq, String targetPackage, int flags, Bundle bundle)135         private void handleBindService(int seq, String targetPackage, int flags, Bundle bundle) {
136             Intent intent = new Intent();
137             intent.setClassName(targetPackage, "com.android.stubs.am.TestService");
138             intent.putExtra(Constants.EXTRA_RECEIVER_CALLBACK, mMessenger);
139             if (bundle != null) {
140                 intent.putExtras(bundle);
141             }
142             synchronized (this) {
143                 if (!mServices.containsKey(targetPackage)) {
144                     MyServiceConnection c = new MyServiceConnection(mCallback);
145                     c.mSeq = seq;
146                     if (!mContext.bindService(intent, c, flags)) {
147                         Log.e(TAG, "Unable to bind to service in " + targetPackage);
148                         sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
149                                 Constants.RESULT_ERROR, null);
150                     } else {
151                         if (VERBOSE) {
152                             Log.i(TAG, "Bind to service " + intent);
153                         }
154                         mServices.put(targetPackage, c);
155                     }
156                 } else {
157                     sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
158                             Constants.RESULT_NO_ERROR, null);
159                 }
160             }
161         }
162 
handleUnbindService(int seq, String target)163         private void handleUnbindService(int seq, String target) {
164             MyServiceConnection c = null;
165             synchronized (this) {
166                 c = mServices.get(target);
167             }
168             if (c != null) {
169                 c.mSeq = seq;
170                 mContext.unbindService(c);
171             }
172         }
173 
acquireProvider(int seq, Uri uri)174         private void acquireProvider(int seq, Uri uri) {
175             ContentResolver resolver = mContext.getContentResolver();
176             IContentProvider provider = resolver.acquireProvider(uri);
177             if (provider != null) {
178                 synchronized (mProviders) {
179                     mProviders.put(uri, provider);
180                 }
181                 sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
182                         Constants.RESULT_NO_ERROR, null);
183             } else {
184                 sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
185                         Constants.RESULT_ERROR, null);
186             }
187         }
188 
releaseProvider(int seq, Uri uri)189         private void releaseProvider(int seq, Uri uri) {
190             ContentResolver resolver = mContext.getContentResolver();
191             IContentProvider provider;
192             synchronized (mProviders) {
193                 provider = mProviders.get(uri);
194             }
195             if (provider != null) {
196                 resolver.releaseProvider(provider);
197                 synchronized (mProviders) {
198                     mProviders.remove(uri);
199                 }
200             }
201             sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
202                     Constants.RESULT_NO_ERROR, null);
203         }
204 
sendBroadcast(final int seq, String targetPackage)205         private void sendBroadcast(final int seq, String targetPackage) {
206             Intent intent = new Intent(Constants.STUB_ACTION_BROADCAST);
207             intent.setPackage(targetPackage);
208             mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
209                 @Override
210                 public void onReceive(Context context, Intent intent) {
211                     sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq,
212                             Constants.RESULT_NO_ERROR, null);
213                 }
214             }, null, 0, null, null);
215         }
216 
startActivity(int seq, String targetPackage)217         private void startActivity(int seq, String targetPackage) {
218             Intent intent = new Intent(Constants.STUB_ACTION_ACTIVITY);
219             intent.setClassName(targetPackage, "com.android.stubs.am.TestActivity");
220             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
221             intent.putExtra(Constants.EXTRA_ARG1, seq);
222             intent.putExtra(Constants.EXTRA_RECEIVER_CALLBACK, mMessenger);
223             mContext.startActivity(intent);
224         }
225 
stopActivity(int seq, String targetPackage)226         private void stopActivity(int seq, String targetPackage) {
227             Intent intent = new Intent(Constants.STUB_ACTION_ACTIVITY);
228             intent.setClassName(targetPackage, "com.android.stubs.am.TestActivity");
229             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
230             intent.putExtra(Constants.EXTRA_REQ_FINISH_ACTIVITY, true);
231             intent.putExtra(Constants.EXTRA_ARG1, seq);
232             intent.putExtra(Constants.EXTRA_RECEIVER_CALLBACK, mMessenger);
233             mContext.startActivity(intent);
234         }
235     };
236 
sendResult(Messenger callback, int what, int seq, int result, Object obj)237     private static void sendResult(Messenger callback, int what, int seq, int result, Object obj) {
238         Message msg = Message.obtain();
239         msg.what = what;
240         msg.arg1 = seq;
241         msg.arg2 = result;
242         msg.obj = obj;
243         try {
244             if (VERBOSE) {
245                 Log.i(TAG, "Sending result seq=" + seq + ", result=" + result);
246             }
247             callback.send(msg);
248         } catch (RemoteException e) {
249             Log.e(TAG, "Error in sending result back", e);
250         }
251         msg.recycle();
252     }
253 
254     private static class MyServiceConnection implements ServiceConnection {
255         private Messenger mCallback;
256         int mSeq;
257 
MyServiceConnection(Messenger callback)258         MyServiceConnection(Messenger callback) {
259             mCallback = callback;
260         }
261 
262         @Override
onServiceConnected(ComponentName name, IBinder service)263         public void onServiceConnected(ComponentName name, IBinder service) {
264             sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, mSeq,
265                     Constants.RESULT_NO_ERROR, null);
266         }
267 
268         @Override
onServiceDisconnected(ComponentName name)269         public void onServiceDisconnected(ComponentName name) {
270             synchronized (sStub) {
271                 MyServiceConnection c = sStub.mServices.remove(name.getPackageName());
272                 if (c != null) {
273                     sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, c.mSeq,
274                             Constants.RESULT_NO_ERROR, null);
275                 }
276             }
277         }
278     }
279 
280     private static Stub sStub = null;
281 
282     @Override
onBind(Intent intent)283     public IBinder onBind(Intent intent) {
284         return new Binder();
285     }
286 
287     @Override
onStartCommand(Intent intent, int flags, int startId)288     public int onStartCommand(Intent intent, int flags, int startId) {
289         Messenger callback = intent.getParcelableExtra(Constants.EXTRA_RECEIVER_CALLBACK);
290         if (sStub == null) {
291             sStub = new Stub(getApplicationContext(), callback);
292         }
293 
294         Bundle extras = new Bundle();
295         extras.putString(Constants.EXTRA_SOURCE_PACKAGE, getPackageName());
296         extras.putBinder(Constants.EXTRA_RECEIVER_CALLBACK, sStub);
297         sendResult(callback, Constants.REPLY_PACKAGE_START_RESULT,
298                 intent.getIntExtra(Constants.EXTRA_SEQ, -1), 0, extras);
299         return START_NOT_STICKY;
300     }
301 }
302