1 /* 2 * Copyright (C) 2018 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 package com.android.internal.util; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.os.Bundle; 21 import android.os.Parcelable; 22 23 import com.android.internal.os.IResultReceiver; 24 25 import java.util.ArrayList; 26 import java.util.concurrent.CountDownLatch; 27 import java.util.concurrent.TimeUnit; 28 29 /** 30 * A {@code IResultReceiver} implementation that can be used to make "sync" Binder calls by blocking 31 * until it receives a result 32 * 33 * @hide 34 */ 35 public final class SyncResultReceiver extends IResultReceiver.Stub { 36 37 private static final String EXTRA = "EXTRA"; 38 39 private final CountDownLatch mLatch = new CountDownLatch(1); 40 private final int mTimeoutMs; 41 private int mResult; 42 private Bundle mBundle; 43 44 /** 45 * Default constructor. 46 * 47 * @param timeoutMs how long to block waiting for {@link IResultReceiver} callbacks. 48 */ SyncResultReceiver(int timeoutMs)49 public SyncResultReceiver(int timeoutMs) { 50 mTimeoutMs = timeoutMs; 51 } 52 waitResult()53 private void waitResult() throws TimeoutException { 54 try { 55 if (!mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS)) { 56 throw new TimeoutException("Not called in " + mTimeoutMs + "ms"); 57 } 58 } catch (InterruptedException e) { 59 Thread.currentThread().interrupt(); 60 throw new TimeoutException("Interrupted"); 61 } 62 } 63 64 /** 65 * Gets the result from an operation that returns an {@code int}. 66 */ getIntResult()67 public int getIntResult() throws TimeoutException { 68 waitResult(); 69 return mResult; 70 } 71 72 /** 73 * Gets the result from an operation that returns an {@code String}. 74 */ 75 @Nullable getStringResult()76 public String getStringResult() throws TimeoutException { 77 waitResult(); 78 return mBundle == null ? null : mBundle.getString(EXTRA); 79 } 80 81 /** 82 * Gets the result from an operation that returns a {@code String[]}. 83 */ 84 @Nullable getStringArrayResult()85 public String[] getStringArrayResult() throws TimeoutException { 86 waitResult(); 87 return mBundle == null ? null : mBundle.getStringArray(EXTRA); 88 } 89 90 /** 91 * Gets the result from an operation that returns a {@code Parcelable}. 92 */ 93 @Nullable getParcelableResult()94 public <P extends Parcelable> P getParcelableResult() throws TimeoutException { 95 waitResult(); 96 return mBundle == null ? null : mBundle.getParcelable(EXTRA); 97 } 98 99 /** 100 * Gets the result from an operation that returns a {@code Parcelable} list. 101 */ 102 @Nullable getParcelableListResult()103 public <P extends Parcelable> ArrayList<P> getParcelableListResult() throws TimeoutException { 104 waitResult(); 105 return mBundle == null ? null : mBundle.getParcelableArrayList(EXTRA); 106 } 107 108 /** 109 * Gets the optional result from an operation that returns an extra {@code int} (besides the 110 * result code). 111 * 112 * @return value set in the bundle, or {@code defaultValue} when not set. 113 */ getOptionalExtraIntResult(int defaultValue)114 public int getOptionalExtraIntResult(int defaultValue) throws TimeoutException { 115 waitResult(); 116 if (mBundle == null || !mBundle.containsKey(EXTRA)) return defaultValue; 117 118 return mBundle.getInt(EXTRA); 119 } 120 121 @Override send(int resultCode, Bundle resultData)122 public void send(int resultCode, Bundle resultData) { 123 mResult = resultCode; 124 mBundle = resultData; 125 mLatch.countDown(); 126 } 127 128 /** 129 * Creates a bundle for a {@code String} value so it can be retrieved by 130 * {@link #getStringResult()}. 131 */ 132 @NonNull bundleFor(@ullable String value)133 public static Bundle bundleFor(@Nullable String value) { 134 final Bundle bundle = new Bundle(); 135 bundle.putString(EXTRA, value); 136 return bundle; 137 } 138 139 /** 140 * Creates a bundle for a {@code String[]} value so it can be retrieved by 141 * {@link #getStringArrayResult()}. 142 */ 143 @NonNull bundleFor(@ullable String[] value)144 public static Bundle bundleFor(@Nullable String[] value) { 145 final Bundle bundle = new Bundle(); 146 bundle.putStringArray(EXTRA, value); 147 return bundle; 148 } 149 150 /** 151 * Creates a bundle for a {@code Parcelable} value so it can be retrieved by 152 * {@link #getParcelableResult()}. 153 */ 154 @NonNull bundleFor(@ullable Parcelable value)155 public static Bundle bundleFor(@Nullable Parcelable value) { 156 final Bundle bundle = new Bundle(); 157 bundle.putParcelable(EXTRA, value); 158 return bundle; 159 } 160 161 /** 162 * Creates a bundle for a {@code Parcelable} list so it can be retrieved by 163 * {@link #getParcelableResult()}. 164 */ 165 @NonNull bundleFor(@ullable ArrayList<? extends Parcelable> value)166 public static Bundle bundleFor(@Nullable ArrayList<? extends Parcelable> value) { 167 final Bundle bundle = new Bundle(); 168 bundle.putParcelableArrayList(EXTRA, value); 169 return bundle; 170 } 171 172 /** 173 * Creates a bundle for an {@code int} value so it can be retrieved by 174 * {@link #getParcelableResult()} - typically used to return an extra {@code int} (as the 1st 175 * is returned as the result code). 176 */ 177 @NonNull bundleFor(int value)178 public static Bundle bundleFor(int value) { 179 final Bundle bundle = new Bundle(); 180 bundle.putInt(EXTRA, value); 181 return bundle; 182 } 183 184 /** @hide */ 185 public static final class TimeoutException extends Exception { TimeoutException(String msg)186 private TimeoutException(String msg) { 187 super(msg); 188 } 189 } 190 } 191