| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.stubs.am; |
| |
| import static com.android.frameworks.perftests.am.util.Constants.COMMAND_ACQUIRE_CONTENT_PROVIDER; |
| import static com.android.frameworks.perftests.am.util.Constants.COMMAND_BIND_SERVICE; |
| import static com.android.frameworks.perftests.am.util.Constants.COMMAND_RELEASE_CONTENT_PROVIDER; |
| import static com.android.frameworks.perftests.am.util.Constants.COMMAND_SEND_BROADCAST; |
| import static com.android.frameworks.perftests.am.util.Constants.COMMAND_START_ACTIVITY; |
| import static com.android.frameworks.perftests.am.util.Constants.COMMAND_STOP_ACTIVITY; |
| import static com.android.frameworks.perftests.am.util.Constants.COMMAND_UNBIND_SERVICE; |
| |
| import android.app.Service; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.IContentProvider; |
| import android.content.Intent; |
| import android.content.ServiceConnection; |
| import android.net.Uri; |
| import android.os.Binder; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.IBinder; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.Messenger; |
| import android.os.RemoteException; |
| import android.util.ArrayMap; |
| import android.util.Log; |
| |
| import com.android.frameworks.perftests.am.util.Constants; |
| import com.android.frameworks.perftests.am.util.ICommandReceiver; |
| |
| public class InitService extends Service { |
| private static final String TAG = "InitService"; |
| public static final boolean VERBOSE = false; |
| |
| private static class Stub extends ICommandReceiver.Stub { |
| private final Context mContext; |
| private final Messenger mCallback; |
| private final Handler mHandler; |
| private final Messenger mMessenger; |
| final ArrayMap<String, MyServiceConnection> mServices = |
| new ArrayMap<String, MyServiceConnection>(); |
| final ArrayMap<Uri, IContentProvider> mProviders = |
| new ArrayMap<Uri, IContentProvider>(); |
| |
| Stub(Context context, Messenger callback) { |
| mContext = context; |
| mCallback = callback; |
| HandlerThread thread = new HandlerThread("result handler"); |
| thread.start(); |
| mHandler = new H(thread.getLooper()); |
| mMessenger = new Messenger(mHandler); |
| } |
| |
| private class H extends Handler { |
| H(Looper looper) { |
| super(looper); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| if (msg.what == Constants.MSG_DEFAULT) { |
| if (VERBOSE) { |
| Log.i(TAG, "H: received seq=" + msg.arg1 + ", result=" + msg.arg2); |
| } |
| sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, msg.arg1, msg.arg2, null); |
| } else if (msg.what == Constants.MSG_UNBIND_DONE) { |
| if (VERBOSE) { |
| Log.i(TAG, "H: received unbind=" + msg.obj); |
| } |
| synchronized (InitService.sStub) { |
| Bundle b = (Bundle) msg.obj; |
| String pkg = b.getString(Constants.EXTRA_SOURCE_PACKAGE, ""); |
| MyServiceConnection c = mServices.remove(pkg); |
| if (c != null) { |
| sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, c.mSeq, |
| Constants.RESULT_NO_ERROR, null); |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void sendCommand(int command, int seq, String sourcePackage, String targetPackage, |
| int flags, Bundle bundle) { |
| if (VERBOSE) { |
| Log.i(TAG, "Received command=" + command + ", seq=" + seq + ", from=" |
| + sourcePackage + ", to=" + targetPackage + ", flags=" + flags); |
| } |
| switch (command) { |
| case COMMAND_BIND_SERVICE: |
| handleBindService(seq, targetPackage, flags, bundle); |
| break; |
| case COMMAND_UNBIND_SERVICE: |
| handleUnbindService(seq, targetPackage); |
| break; |
| case COMMAND_ACQUIRE_CONTENT_PROVIDER: |
| acquireProvider(seq, bundle.getParcelable(Constants.EXTRA_URI)); |
| break; |
| case COMMAND_RELEASE_CONTENT_PROVIDER: |
| releaseProvider(seq, bundle.getParcelable(Constants.EXTRA_URI)); |
| break; |
| case COMMAND_SEND_BROADCAST: |
| sendBroadcast(seq, targetPackage); |
| break; |
| case COMMAND_START_ACTIVITY: |
| startActivity(seq, targetPackage); |
| break; |
| case COMMAND_STOP_ACTIVITY: |
| stopActivity(seq, targetPackage); |
| break; |
| } |
| } |
| |
| private void handleBindService(int seq, String targetPackage, int flags, Bundle bundle) { |
| Intent intent = new Intent(); |
| intent.setClassName(targetPackage, "com.android.stubs.am.TestService"); |
| intent.putExtra(Constants.EXTRA_RECEIVER_CALLBACK, mMessenger); |
| if (bundle != null) { |
| intent.putExtras(bundle); |
| } |
| synchronized (this) { |
| if (!mServices.containsKey(targetPackage)) { |
| MyServiceConnection c = new MyServiceConnection(mCallback); |
| c.mSeq = seq; |
| if (!mContext.bindService(intent, c, flags)) { |
| Log.e(TAG, "Unable to bind to service in " + targetPackage); |
| sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq, |
| Constants.RESULT_ERROR, null); |
| } else { |
| if (VERBOSE) { |
| Log.i(TAG, "Bind to service " + intent); |
| } |
| mServices.put(targetPackage, c); |
| } |
| } else { |
| sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq, |
| Constants.RESULT_NO_ERROR, null); |
| } |
| } |
| } |
| |
| private void handleUnbindService(int seq, String target) { |
| MyServiceConnection c = null; |
| synchronized (this) { |
| c = mServices.get(target); |
| } |
| if (c != null) { |
| c.mSeq = seq; |
| mContext.unbindService(c); |
| } |
| } |
| |
| private void acquireProvider(int seq, Uri uri) { |
| ContentResolver resolver = mContext.getContentResolver(); |
| IContentProvider provider = resolver.acquireProvider(uri); |
| if (provider != null) { |
| synchronized (mProviders) { |
| mProviders.put(uri, provider); |
| } |
| sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq, |
| Constants.RESULT_NO_ERROR, null); |
| } else { |
| sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq, |
| Constants.RESULT_ERROR, null); |
| } |
| } |
| |
| private void releaseProvider(int seq, Uri uri) { |
| ContentResolver resolver = mContext.getContentResolver(); |
| IContentProvider provider; |
| synchronized (mProviders) { |
| provider = mProviders.get(uri); |
| } |
| if (provider != null) { |
| resolver.releaseProvider(provider); |
| synchronized (mProviders) { |
| mProviders.remove(uri); |
| } |
| } |
| sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq, |
| Constants.RESULT_NO_ERROR, null); |
| } |
| |
| private void sendBroadcast(final int seq, String targetPackage) { |
| Intent intent = new Intent(Constants.STUB_ACTION_BROADCAST); |
| intent.setPackage(targetPackage); |
| mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, seq, |
| Constants.RESULT_NO_ERROR, null); |
| } |
| }, null, 0, null, null); |
| } |
| |
| private void startActivity(int seq, String targetPackage) { |
| Intent intent = new Intent(Constants.STUB_ACTION_ACTIVITY); |
| intent.setClassName(targetPackage, "com.android.stubs.am.TestActivity"); |
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); |
| intent.putExtra(Constants.EXTRA_ARG1, seq); |
| intent.putExtra(Constants.EXTRA_RECEIVER_CALLBACK, mMessenger); |
| mContext.startActivity(intent); |
| } |
| |
| private void stopActivity(int seq, String targetPackage) { |
| Intent intent = new Intent(Constants.STUB_ACTION_ACTIVITY); |
| intent.setClassName(targetPackage, "com.android.stubs.am.TestActivity"); |
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); |
| intent.putExtra(Constants.EXTRA_REQ_FINISH_ACTIVITY, true); |
| intent.putExtra(Constants.EXTRA_ARG1, seq); |
| intent.putExtra(Constants.EXTRA_RECEIVER_CALLBACK, mMessenger); |
| mContext.startActivity(intent); |
| } |
| }; |
| |
| private static void sendResult(Messenger callback, int what, int seq, int result, Object obj) { |
| Message msg = Message.obtain(); |
| msg.what = what; |
| msg.arg1 = seq; |
| msg.arg2 = result; |
| msg.obj = obj; |
| try { |
| if (VERBOSE) { |
| Log.i(TAG, "Sending result seq=" + seq + ", result=" + result); |
| } |
| callback.send(msg); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Error in sending result back", e); |
| } |
| msg.recycle(); |
| } |
| |
| private static class MyServiceConnection implements ServiceConnection { |
| private Messenger mCallback; |
| int mSeq; |
| |
| MyServiceConnection(Messenger callback) { |
| mCallback = callback; |
| } |
| |
| @Override |
| public void onServiceConnected(ComponentName name, IBinder service) { |
| sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, mSeq, |
| Constants.RESULT_NO_ERROR, null); |
| } |
| |
| @Override |
| public void onServiceDisconnected(ComponentName name) { |
| synchronized (sStub) { |
| MyServiceConnection c = sStub.mServices.remove(name.getPackageName()); |
| if (c != null) { |
| sendResult(mCallback, Constants.REPLY_COMMAND_RESULT, c.mSeq, |
| Constants.RESULT_NO_ERROR, null); |
| } |
| } |
| } |
| } |
| |
| private static Stub sStub = null; |
| |
| @Override |
| public IBinder onBind(Intent intent) { |
| return new Binder(); |
| } |
| |
| @Override |
| public int onStartCommand(Intent intent, int flags, int startId) { |
| Messenger callback = intent.getParcelableExtra(Constants.EXTRA_RECEIVER_CALLBACK); |
| if (sStub == null) { |
| sStub = new Stub(getApplicationContext(), callback); |
| } |
| |
| Bundle extras = new Bundle(); |
| extras.putString(Constants.EXTRA_SOURCE_PACKAGE, getPackageName()); |
| extras.putBinder(Constants.EXTRA_RECEIVER_CALLBACK, sStub); |
| sendResult(callback, Constants.REPLY_PACKAGE_START_RESULT, |
| intent.getIntExtra(Constants.EXTRA_SEQ, -1), 0, extras); |
| return START_NOT_STICKY; |
| } |
| } |