Home | History | Annotate | Download | only in content
      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