1 /* 2 * Copyright (C) 2012 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.launcher3; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.util.Log; 24 25 import java.io.*; 26 import java.util.ArrayList; 27 28 public class Stats { 29 private static final boolean DEBUG_BROADCASTS = false; 30 private static final String TAG = "Launcher3/Stats"; 31 32 private static final boolean LOCAL_LAUNCH_LOG = true; 33 34 public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH"; 35 public static final String EXTRA_INTENT = "intent"; 36 public static final String EXTRA_CONTAINER = "container"; 37 public static final String EXTRA_SCREEN = "screen"; 38 public static final String EXTRA_CELLX = "cellX"; 39 public static final String EXTRA_CELLY = "cellY"; 40 41 private static final String LOG_FILE_NAME = "launches.log"; 42 private static final int LOG_VERSION = 1; 43 private static final int LOG_TAG_VERSION = 0x1; 44 private static final int LOG_TAG_LAUNCH = 0x1000; 45 46 private static final String STATS_FILE_NAME = "stats.log"; 47 private static final int STATS_VERSION = 1; 48 private static final int INITIAL_STATS_SIZE = 100; 49 50 // TODO: delayed/batched writes 51 private static final boolean FLUSH_IMMEDIATELY = true; 52 53 private final Launcher mLauncher; 54 55 private final String mLaunchBroadcastPermission; 56 57 DataOutputStream mLog; 58 59 ArrayList<String> mIntents; 60 ArrayList<Integer> mHistogram; 61 62 public Stats(Launcher launcher) { 63 mLauncher = launcher; 64 65 mLaunchBroadcastPermission = 66 launcher.getResources().getString(R.string.receive_launch_broadcasts_permission); 67 68 loadStats(); 69 70 if (LOCAL_LAUNCH_LOG) { 71 try { 72 mLog = new DataOutputStream(mLauncher.openFileOutput(LOG_FILE_NAME, Context.MODE_APPEND)); 73 mLog.writeInt(LOG_TAG_VERSION); 74 mLog.writeInt(LOG_VERSION); 75 } catch (FileNotFoundException e) { 76 Log.e(TAG, "unable to create stats log: " + e); 77 mLog = null; 78 } catch (IOException e) { 79 Log.e(TAG, "unable to write to stats log: " + e); 80 mLog = null; 81 } 82 } 83 84 if (DEBUG_BROADCASTS) { 85 launcher.registerReceiver( 86 new BroadcastReceiver() { 87 @Override 88 public void onReceive(Context context, Intent intent) { 89 android.util.Log.v("Stats", "got broadcast: " + intent + " for launched intent: " 90 + intent.getStringExtra(EXTRA_INTENT)); 91 } 92 }, 93 new IntentFilter(ACTION_LAUNCH), 94 mLaunchBroadcastPermission, 95 null 96 ); 97 } 98 } 99 100 public void incrementLaunch(String intentStr) { 101 int pos = mIntents.indexOf(intentStr); 102 if (pos < 0) { 103 mIntents.add(intentStr); 104 mHistogram.add(1); 105 } else { 106 mHistogram.set(pos, mHistogram.get(pos) + 1); 107 } 108 } 109 110 public void recordLaunch(Intent intent) { 111 recordLaunch(intent, null); 112 } 113 114 public void recordLaunch(Intent intent, ShortcutInfo shortcut) { 115 intent = new Intent(intent); 116 intent.setSourceBounds(null); 117 118 final String flat = intent.toUri(0); 119 120 Intent broadcastIntent = new Intent(ACTION_LAUNCH).putExtra(EXTRA_INTENT, flat); 121 if (shortcut != null) { 122 broadcastIntent.putExtra(EXTRA_CONTAINER, shortcut.container) 123 .putExtra(EXTRA_SCREEN, shortcut.screenId) 124 .putExtra(EXTRA_CELLX, shortcut.cellX) 125 .putExtra(EXTRA_CELLY, shortcut.cellY); 126 } 127 mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission); 128 129 incrementLaunch(flat); 130 131 if (FLUSH_IMMEDIATELY) { 132 saveStats(); 133 } 134 135 if (LOCAL_LAUNCH_LOG && mLog != null) { 136 try { 137 mLog.writeInt(LOG_TAG_LAUNCH); 138 mLog.writeLong(System.currentTimeMillis()); 139 if (shortcut == null) { 140 mLog.writeShort(0); 141 mLog.writeShort(0); 142 mLog.writeShort(0); 143 mLog.writeShort(0); 144 } else { 145 mLog.writeShort((short) shortcut.container); 146 mLog.writeShort((short) shortcut.screenId); 147 mLog.writeShort((short) shortcut.cellX); 148 mLog.writeShort((short) shortcut.cellY); 149 } 150 mLog.writeUTF(flat); 151 if (FLUSH_IMMEDIATELY) { 152 mLog.flush(); // TODO: delayed writes 153 } 154 } catch (IOException e) { 155 e.printStackTrace(); 156 } 157 } 158 } 159 160 private void saveStats() { 161 DataOutputStream stats = null; 162 try { 163 stats = new DataOutputStream(mLauncher.openFileOutput(STATS_FILE_NAME + ".tmp", Context.MODE_PRIVATE)); 164 stats.writeInt(STATS_VERSION); 165 final int N = mHistogram.size(); 166 stats.writeInt(N); 167 for (int i=0; i<N; i++) { 168 stats.writeUTF(mIntents.get(i)); 169 stats.writeInt(mHistogram.get(i)); 170 } 171 stats.close(); 172 stats = null; 173 mLauncher.getFileStreamPath(STATS_FILE_NAME + ".tmp") 174 .renameTo(mLauncher.getFileStreamPath(STATS_FILE_NAME)); 175 } catch (FileNotFoundException e) { 176 Log.e(TAG, "unable to create stats data: " + e); 177 } catch (IOException e) { 178 Log.e(TAG, "unable to write to stats data: " + e); 179 } finally { 180 if (stats != null) { 181 try { 182 stats.close(); 183 } catch (IOException e) { } 184 } 185 } 186 } 187 188 private void loadStats() { 189 mIntents = new ArrayList<String>(INITIAL_STATS_SIZE); 190 mHistogram = new ArrayList<Integer>(INITIAL_STATS_SIZE); 191 DataInputStream stats = null; 192 try { 193 stats = new DataInputStream(mLauncher.openFileInput(STATS_FILE_NAME)); 194 final int version = stats.readInt(); 195 if (version == STATS_VERSION) { 196 final int N = stats.readInt(); 197 for (int i=0; i<N; i++) { 198 final String pkg = stats.readUTF(); 199 final int count = stats.readInt(); 200 mIntents.add(pkg); 201 mHistogram.add(count); 202 } 203 } 204 } catch (FileNotFoundException e) { 205 // not a problem 206 } catch (IOException e) { 207 // more of a problem 208 209 } finally { 210 if (stats != null) { 211 try { 212 stats.close(); 213 } catch (IOException e) { } 214 } 215 } 216 } 217 } 218