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 volatile boolean mBindRequested; 60 private IEphemeralResolver mRemoteInstance; 61 62 public EphemeralResolverConnection(Context context, ComponentName componentName) { 63 mContext = context; 64 mIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE).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 if (!mBindRequested) { 116 mBindRequested = true; 117 mContext.bindServiceAsUser(mIntent, mServiceConnection, 118 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, UserHandle.SYSTEM); 119 } 120 121 final long startMillis = SystemClock.uptimeMillis(); 122 while (true) { 123 if (mRemoteInstance != null) { 124 break; 125 } 126 final long elapsedMillis = SystemClock.uptimeMillis() - startMillis; 127 final long remainingMillis = BIND_SERVICE_TIMEOUT_MS - elapsedMillis; 128 if (remainingMillis <= 0) { 129 throw new TimeoutException("Didn't bind to resolver in time."); 130 } 131 try { 132 mLock.wait(remainingMillis); 133 } catch (InterruptedException ie) { 134 /* ignore */ 135 } 136 } 137 138 mLock.notifyAll(); 139 } 140 141 private void throwIfCalledOnMainThread() { 142 if (Thread.currentThread() == mContext.getMainLooper().getThread()) { 143 throw new RuntimeException("Cannot invoke on the main thread"); 144 } 145 } 146 147 private final class MyServiceConnection implements ServiceConnection { 148 @Override 149 public void onServiceConnected(ComponentName name, IBinder service) { 150 synchronized (mLock) { 151 mRemoteInstance = IEphemeralResolver.Stub.asInterface(service); 152 mLock.notifyAll(); 153 } 154 } 155 156 @Override 157 public void onServiceDisconnected(ComponentName name) { 158 synchronized (mLock) { 159 mRemoteInstance = null; 160 } 161 } 162 } 163 164 private static final class GetEphemeralResolveInfoCaller 165 extends TimedRemoteCaller<List<EphemeralResolveInfo>> { 166 private final IRemoteCallback mCallback; 167 168 public GetEphemeralResolveInfoCaller() { 169 super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS); 170 mCallback = new IRemoteCallback.Stub() { 171 @Override 172 public void sendResult(Bundle data) throws RemoteException { 173 final ArrayList<EphemeralResolveInfo> resolveList = 174 data.getParcelableArrayList( 175 EphemeralResolverService.EXTRA_RESOLVE_INFO); 176 int sequence = 177 data.getInt(EphemeralResolverService.EXTRA_SEQUENCE, -1); 178 onRemoteMethodResult(resolveList, sequence); 179 } 180 }; 181 } 182 183 public List<EphemeralResolveInfo> getEphemeralResolveInfoList( 184 IEphemeralResolver target, int hashPrefix[], int prefixMask) 185 throws RemoteException, TimeoutException { 186 final int sequence = onBeforeRemoteCall(); 187 target.getEphemeralResolveInfoList(mCallback, hashPrefix, prefixMask, sequence); 188 return getResultTimed(sequence); 189 } 190 } 191 } 192