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