blob: 68b465aaf816b5051f85f98d9d6ed77268b03666 [file] [log] [blame]
Todd Kennedyb8a279e2015-11-18 09:59:47 -08001/*
2 * Copyright (C) 2015 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 com.android.server.pm;
18
Todd Kennedy31b95e02016-08-04 14:27:15 -070019import android.app.EphemeralResolverService;
20import android.app.IEphemeralResolver;
Todd Kennedyb8a279e2015-11-18 09:59:47 -080021import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.ServiceConnection;
Todd Kennedy7440f172015-12-09 14:31:22 -080025import android.content.pm.EphemeralResolveInfo;
Todd Kennedyb8a279e2015-11-18 09:59:47 -080026import android.os.Build;
27import android.os.Bundle;
28import android.os.IBinder;
29import android.os.IRemoteCallback;
30import android.os.RemoteException;
31import android.os.SystemClock;
32import android.os.UserHandle;
33import android.util.TimedRemoteCaller;
34
Todd Kennedyb8a279e2015-11-18 09:59:47 -080035import java.io.FileDescriptor;
36import java.io.PrintWriter;
37import java.util.ArrayList;
38import java.util.List;
39import java.util.concurrent.TimeoutException;
40
41/**
42 * Represents a remote ephemeral resolver. It is responsible for binding to the remote
43 * service and handling all interactions in a timely manner.
44 * @hide
45 */
46final class EphemeralResolverConnection {
47 // This is running in a critical section and the timeout must be sufficiently low
48 private static final long BIND_SERVICE_TIMEOUT_MS =
49 ("eng".equals(Build.TYPE)) ? 300 : 200;
50
51 private final Object mLock = new Object();
52 private final GetEphemeralResolveInfoCaller mGetEphemeralResolveInfoCaller =
53 new GetEphemeralResolveInfoCaller();
54 private final ServiceConnection mServiceConnection = new MyServiceConnection();
55 private final Context mContext;
56 /** Intent used to bind to the service */
57 private final Intent mIntent;
58
59 private IEphemeralResolver mRemoteInstance;
60
61 public EphemeralResolverConnection(Context context, ComponentName componentName) {
62 mContext = context;
Todd Kennedy7d59cb82016-09-08 13:49:05 -070063 mIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE).setComponent(componentName);
Todd Kennedyb8a279e2015-11-18 09:59:47 -080064 }
65
Todd Kennedya97045b2016-06-10 16:42:00 -070066 public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(
67 int hashPrefix[], int prefixMask) {
Todd Kennedyb8a279e2015-11-18 09:59:47 -080068 throwIfCalledOnMainThread();
69 try {
70 return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList(
Todd Kennedya97045b2016-06-10 16:42:00 -070071 getRemoteInstanceLazy(), hashPrefix, prefixMask);
Todd Kennedyb8a279e2015-11-18 09:59:47 -080072 } catch (RemoteException re) {
73 } catch (TimeoutException te) {
74 } finally {
75 synchronized (mLock) {
76 mLock.notifyAll();
77 }
78 }
79 return null;
80 }
81
82 public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
83 synchronized (mLock) {
84 pw.append(prefix).append("bound=")
85 .append((mRemoteInstance != null) ? "true" : "false").println();
86
87 pw.flush();
88
89 try {
90 getRemoteInstanceLazy().asBinder().dump(fd, new String[] { prefix });
91 } catch (TimeoutException te) {
92 /* ignore */
93 } catch (RemoteException re) {
94 /* ignore */
95 }
96 }
97 }
98
99 private IEphemeralResolver getRemoteInstanceLazy() throws TimeoutException {
100 synchronized (mLock) {
101 if (mRemoteInstance != null) {
102 return mRemoteInstance;
103 }
104 bindLocked();
105 return mRemoteInstance;
106 }
107 }
108
109 private void bindLocked() throws TimeoutException {
110 if (mRemoteInstance != null) {
111 return;
112 }
113
114 mContext.bindServiceAsUser(mIntent, mServiceConnection,
115 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, UserHandle.SYSTEM);
116
117 final long startMillis = SystemClock.uptimeMillis();
118 while (true) {
119 if (mRemoteInstance != null) {
120 break;
121 }
122 final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
123 final long remainingMillis = BIND_SERVICE_TIMEOUT_MS - elapsedMillis;
124 if (remainingMillis <= 0) {
125 throw new TimeoutException("Didn't bind to resolver in time.");
126 }
127 try {
128 mLock.wait(remainingMillis);
129 } catch (InterruptedException ie) {
130 /* ignore */
131 }
132 }
133
134 mLock.notifyAll();
135 }
136
137 private void throwIfCalledOnMainThread() {
138 if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
139 throw new RuntimeException("Cannot invoke on the main thread");
140 }
141 }
142
143 private final class MyServiceConnection implements ServiceConnection {
144 @Override
145 public void onServiceConnected(ComponentName name, IBinder service) {
146 synchronized (mLock) {
147 mRemoteInstance = IEphemeralResolver.Stub.asInterface(service);
148 mLock.notifyAll();
149 }
150 }
151
152 @Override
153 public void onServiceDisconnected(ComponentName name) {
154 synchronized (mLock) {
155 mRemoteInstance = null;
156 }
157 }
158 }
159
160 private static final class GetEphemeralResolveInfoCaller
161 extends TimedRemoteCaller<List<EphemeralResolveInfo>> {
162 private final IRemoteCallback mCallback;
163
164 public GetEphemeralResolveInfoCaller() {
165 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
166 mCallback = new IRemoteCallback.Stub() {
167 @Override
168 public void sendResult(Bundle data) throws RemoteException {
169 final ArrayList<EphemeralResolveInfo> resolveList =
170 data.getParcelableArrayList(
171 EphemeralResolverService.EXTRA_RESOLVE_INFO);
172 int sequence =
173 data.getInt(EphemeralResolverService.EXTRA_SEQUENCE, -1);
174 onRemoteMethodResult(resolveList, sequence);
175 }
176 };
177 }
178
179 public List<EphemeralResolveInfo> getEphemeralResolveInfoList(
Todd Kennedya97045b2016-06-10 16:42:00 -0700180 IEphemeralResolver target, int hashPrefix[], int prefixMask)
Todd Kennedyb8a279e2015-11-18 09:59:47 -0800181 throws RemoteException, TimeoutException {
182 final int sequence = onBeforeRemoteCall();
Todd Kennedya97045b2016-06-10 16:42:00 -0700183 target.getEphemeralResolveInfoList(mCallback, hashPrefix, prefixMask, sequence);
Todd Kennedyb8a279e2015-11-18 09:59:47 -0800184 return getResultTimed(sequence);
185 }
186 }
187}