OSDN Git Service

Move EphemeralResolverService
[android-x86/frameworks-base.git] / services / core / java / com / android / server / pm / EphemeralResolverConnection.java
1 /*
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
17 package com.android.server.pm;
18
19 import android.app.EphemeralResolverService;
20 import android.app.IEphemeralResolver;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.content.pm.EphemeralResolveInfo;
26 import android.os.Build;
27 import android.os.Bundle;
28 import android.os.IBinder;
29 import android.os.IRemoteCallback;
30 import android.os.RemoteException;
31 import android.os.SystemClock;
32 import android.os.UserHandle;
33 import android.util.TimedRemoteCaller;
34
35 import java.io.FileDescriptor;
36 import java.io.PrintWriter;
37 import java.util.ArrayList;
38 import java.util.List;
39 import 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  */
46 final 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;
63         mIntent = new Intent().setComponent(componentName);
64     }
65
66     public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(
67             int hashPrefix[], int prefixMask) {
68         throwIfCalledOnMainThread();
69         try {
70             return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList(
71                     getRemoteInstanceLazy(), hashPrefix, prefixMask);
72         } 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(
180                 IEphemeralResolver target, int hashPrefix[], int prefixMask)
181                         throws RemoteException, TimeoutException {
182             final int sequence = onBeforeRemoteCall();
183             target.getEphemeralResolveInfoList(mCallback, hashPrefix, prefixMask, sequence);
184             return getResultTimed(sequence);
185         }
186     }
187 }