OSDN Git Service

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