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 17package com.android.server.pm; 18 19import android.content.ComponentName; 20import android.content.Context; 21import android.content.Intent; 22import android.content.ServiceConnection; 23import android.content.pm.EphemeralResolveInfo; 24import android.os.Build; 25import android.os.Bundle; 26import android.os.IBinder; 27import android.os.IRemoteCallback; 28import android.os.RemoteException; 29import android.os.SystemClock; 30import android.os.UserHandle; 31import android.util.TimedRemoteCaller; 32 33import com.android.internal.app.EphemeralResolverService; 34import com.android.internal.app.IEphemeralResolver; 35 36import java.io.FileDescriptor; 37import java.io.PrintWriter; 38import java.util.ArrayList; 39import java.util.List; 40import 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 */ 47final 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(int hashPrefix) { 68 throwIfCalledOnMainThread(); 69 try { 70 return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList( 71 getRemoteInstanceLazy(), hashPrefix); 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) 181 throws RemoteException, TimeoutException { 182 final int sequence = onBeforeRemoteCall(); 183 target.getEphemeralResolveInfoList(mCallback, hashPrefix, sequence); 184 return getResultTimed(sequence); 185 } 186 } 187} 188