1 /* 2 * Copyright (C) 2019 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 android.content; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.res.AssetFileDescriptor; 22 import android.database.Cursor; 23 import android.database.DatabaseUtils; 24 import android.net.Uri; 25 import android.os.Binder; 26 import android.os.Bundle; 27 import android.os.CancellationSignal; 28 import android.os.ParcelFileDescriptor; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 import java.io.FileNotFoundException; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 36 /** 37 * Instance of {@link ContentInterface} that logs all inputs and outputs while 38 * delegating to another {@link ContentInterface}. 39 * 40 * @hide 41 */ 42 public class LoggingContentInterface implements ContentInterface { 43 private final String tag; 44 private final ContentInterface delegate; 45 46 public LoggingContentInterface(String tag, ContentInterface delegate) { 47 this.tag = tag; 48 this.delegate = delegate; 49 } 50 51 private class Logger implements AutoCloseable { 52 private final StringBuilder sb = new StringBuilder(); 53 54 public Logger(String method, Object... args) { 55 // First, force-unparcel any bundles so we can log them 56 for (Object arg : args) { 57 if (arg instanceof Bundle) { 58 ((Bundle) arg).size(); 59 } 60 } 61 62 sb.append("callingUid=").append(Binder.getCallingUid()).append(' '); 63 sb.append(method); 64 sb.append('(').append(deepToString(args)).append(')'); 65 } 66 67 private String deepToString(Object value) { 68 if (value != null && value.getClass().isArray()) { 69 return Arrays.deepToString((Object[]) value); 70 } else { 71 return String.valueOf(value); 72 } 73 } 74 75 public <T> T setResult(T res) { 76 if (res instanceof Cursor) { 77 sb.append('\n'); 78 DatabaseUtils.dumpCursor((Cursor) res, sb); 79 } else { 80 sb.append(" = ").append(deepToString(res)); 81 } 82 return res; 83 } 84 85 @Override 86 public void close() { 87 Log.v(tag, sb.toString()); 88 } 89 } 90 91 @Override 92 public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection, 93 @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) 94 throws RemoteException { 95 try (Logger l = new Logger("query", uri, projection, queryArgs, cancellationSignal)) { 96 try { 97 return l.setResult(delegate.query(uri, projection, queryArgs, cancellationSignal)); 98 } catch (Exception res) { 99 l.setResult(res); 100 throw res; 101 } 102 } 103 } 104 105 @Override 106 public @Nullable String getType(@NonNull Uri uri) throws RemoteException { 107 try (Logger l = new Logger("getType", uri)) { 108 try { 109 return l.setResult(delegate.getType(uri)); 110 } catch (Exception res) { 111 l.setResult(res); 112 throw res; 113 } 114 } 115 } 116 117 @Override 118 public @Nullable String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter) 119 throws RemoteException { 120 try (Logger l = new Logger("getStreamTypes", uri, mimeTypeFilter)) { 121 try { 122 return l.setResult(delegate.getStreamTypes(uri, mimeTypeFilter)); 123 } catch (Exception res) { 124 l.setResult(res); 125 throw res; 126 } 127 } 128 } 129 130 @Override 131 public @Nullable Uri canonicalize(@NonNull Uri uri) throws RemoteException { 132 try (Logger l = new Logger("canonicalize", uri)) { 133 try { 134 return l.setResult(delegate.canonicalize(uri)); 135 } catch (Exception res) { 136 l.setResult(res); 137 throw res; 138 } 139 } 140 } 141 142 @Override 143 public @Nullable Uri uncanonicalize(@NonNull Uri uri) throws RemoteException { 144 try (Logger l = new Logger("uncanonicalize", uri)) { 145 try { 146 return l.setResult(delegate.uncanonicalize(uri)); 147 } catch (Exception res) { 148 l.setResult(res); 149 throw res; 150 } 151 } 152 } 153 154 @Override 155 public boolean refresh(@NonNull Uri uri, @Nullable Bundle args, 156 @Nullable CancellationSignal cancellationSignal) throws RemoteException { 157 try (Logger l = new Logger("refresh", uri, args, cancellationSignal)) { 158 try { 159 return l.setResult(delegate.refresh(uri, args, cancellationSignal)); 160 } catch (Exception res) { 161 l.setResult(res); 162 throw res; 163 } 164 } 165 } 166 167 @Override 168 public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues) 169 throws RemoteException { 170 try (Logger l = new Logger("insert", uri, initialValues)) { 171 try { 172 return l.setResult(delegate.insert(uri, initialValues)); 173 } catch (Exception res) { 174 l.setResult(res); 175 throw res; 176 } 177 } 178 } 179 180 @Override 181 public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] initialValues) 182 throws RemoteException { 183 try (Logger l = new Logger("bulkInsert", uri, initialValues)) { 184 try { 185 return l.setResult(delegate.bulkInsert(uri, initialValues)); 186 } catch (Exception res) { 187 l.setResult(res); 188 throw res; 189 } 190 } 191 } 192 193 @Override 194 public int delete(@NonNull Uri uri, @Nullable String selection, 195 @Nullable String[] selectionArgs) throws RemoteException { 196 try (Logger l = new Logger("delete", uri, selection, selectionArgs)) { 197 try { 198 return l.setResult(delegate.delete(uri, selection, selectionArgs)); 199 } catch (Exception res) { 200 l.setResult(res); 201 throw res; 202 } 203 } 204 } 205 206 @Override 207 public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, 208 @Nullable String[] selectionArgs) throws RemoteException { 209 try (Logger l = new Logger("update", uri, values, selection, selectionArgs)) { 210 try { 211 return l.setResult(delegate.update(uri, values, selection, selectionArgs)); 212 } catch (Exception res) { 213 l.setResult(res); 214 throw res; 215 } 216 } 217 } 218 219 @Override 220 public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode, 221 @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException { 222 try (Logger l = new Logger("openFile", uri, mode, signal)) { 223 try { 224 return l.setResult(delegate.openFile(uri, mode, signal)); 225 } catch (Exception res) { 226 l.setResult(res); 227 throw res; 228 } 229 } 230 } 231 232 @Override 233 public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode, 234 @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException { 235 try (Logger l = new Logger("openAssetFile", uri, mode, signal)) { 236 try { 237 return l.setResult(delegate.openAssetFile(uri, mode, signal)); 238 } catch (Exception res) { 239 l.setResult(res); 240 throw res; 241 } 242 } 243 } 244 245 @Override 246 public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri, 247 @NonNull String mimeTypeFilter, @Nullable Bundle opts, 248 @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException { 249 try (Logger l = new Logger("openTypedAssetFile", uri, mimeTypeFilter, opts, signal)) { 250 try { 251 return l.setResult(delegate.openTypedAssetFile(uri, mimeTypeFilter, opts, signal)); 252 } catch (Exception res) { 253 l.setResult(res); 254 throw res; 255 } 256 } 257 } 258 259 @Override 260 public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority, 261 @NonNull ArrayList<ContentProviderOperation> operations) 262 throws RemoteException, OperationApplicationException { 263 try (Logger l = new Logger("applyBatch", authority, operations)) { 264 try { 265 return l.setResult(delegate.applyBatch(authority, operations)); 266 } catch (Exception res) { 267 l.setResult(res); 268 throw res; 269 } 270 } 271 } 272 273 @Override 274 public @Nullable Bundle call(@NonNull String authority, @NonNull String method, 275 @Nullable String arg, @Nullable Bundle extras) throws RemoteException { 276 try (Logger l = new Logger("call", authority, method, arg, extras)) { 277 try { 278 return l.setResult(delegate.call(authority, method, arg, extras)); 279 } catch (Exception res) { 280 l.setResult(res); 281 throw res; 282 } 283 } 284 } 285 } 286