@Nonnull private static LocalServerSocket bindToSocket(String address) throws IOException { int retries = MAX_BIND_RETRIES; IOException firstException = null; do { try { if (LogUtil.isLoggable(Log.DEBUG)) { LogUtil.d("Trying to bind to @" + address); } return new LocalServerSocket(address); } catch (BindException be) { LogUtil.w(be, "Binding error, sleep " + TIME_BETWEEN_BIND_RETRIES_MS + " ms..."); if (firstException == null) { firstException = be; } Util.sleepUninterruptibly(TIME_BETWEEN_BIND_RETRIES_MS); } } while (retries-- > 0); throw firstException; }
private static void enforcePermission(Context context, LocalSocket peer) throws IOException, PeerAuthorizationException { Credentials credentials = peer.getPeerCredentials(); int uid = credentials.getUid(); int pid = credentials.getPid(); if (LogUtil.isLoggable(Log.VERBOSE)) { LogUtil.v("Got request from uid=%d, pid=%d", uid, pid); } String requiredPermission = Manifest.permission.DUMP; int checkResult = context.checkPermission(requiredPermission, pid, uid); if (checkResult != PackageManager.PERMISSION_GRANTED) { throw new PeerAuthorizationException( "Peer pid=" + pid + ", uid=" + uid + " does not have " + requiredPermission); } }
private void closeAll(int mostImportantIndex) throws IOException { IOException exceptionToThrow = null; for (int i = 0; i < mStreams.length; i++) { try { mStreams[i].close(); } catch (IOException e) { IOException previousException = exceptionToThrow; if (i == mostImportantIndex || exceptionToThrow == null) { exceptionToThrow = e; } if (previousException != null && previousException != exceptionToThrow) { LogUtil.w(previousException, "Suppressing exception"); } } } }
@ChromeDevtoolsMethod public void highlightNode(JsonRpcPeer peer, JSONObject params) { final HighlightNodeRequest request = mObjectMapper.convertValue(params, HighlightNodeRequest.class); if (request.nodeId == null) { LogUtil.w("DOM.highlightNode was not given a nodeId; JS objectId is not supported"); return; } final RGBAColor contentColor = request.highlightConfig.contentColor; if (contentColor == null) { LogUtil.w("DOM.highlightNode was not given a color to highlight with"); return; } mDocument.postAndWait(new Runnable() { @Override public void run() { Object element = mDocument.getElementForNodeId(request.nodeId); if (element != null) { mDocument.highlightElement(element, contentColor.getColor()); } } }); }
@ChromeDevtoolsMethod public GetSearchResultsResponse getSearchResults(JsonRpcPeer peer, JSONObject params) { final GetSearchResultsRequest request = mObjectMapper.convertValue( params, GetSearchResultsRequest.class); if (request.searchId == null) { LogUtil.w("searchId may not be null"); return null; } final List<Integer> results = mSearchResults.get(request.searchId); if (results == null) { LogUtil.w("\"" + request.searchId + "\" is not a valid reference to a search result"); return null; } final List<Integer> resultsRange = results.subList(request.fromIndex, request.toIndex); final GetSearchResultsResponse response = new GetSearchResultsResponse(); response.nodeIds = resultsRange; return response; }
private void updateTree() { long startTimeMs = SystemClock.elapsedRealtime(); ShadowDocument.Update docUpdate = createShadowDocumentUpdate(); boolean isEmpty = docUpdate.isEmpty(); if (isEmpty) { docUpdate.abandon(); } else { applyDocumentUpdate(docUpdate); } long deltaMs = SystemClock.elapsedRealtime() - startTimeMs; LogUtil.d( "Document.updateTree() completed in %s ms%s", Long.toString(deltaMs), isEmpty ? " (no changes)" : ""); }
private @NonNull ScriptableObject initJsScope(@NonNull Context jsContext) { // Set the main Rhino goodies ImporterTopLevel importerTopLevel = new ImporterTopLevel(jsContext); ScriptableObject scope = jsContext.initStandardObjects(importerTopLevel, false); ScriptableObject.putProperty(scope, "context", Context.javaToJS(mContext, scope)); try { importClasses(jsContext, scope); importPackages(jsContext, scope); importConsole(scope); importVariables(scope); importFunctions(scope); } catch (StethoJsException e) { String message = String.format("%s\n%s", e.getMessage(), Log.getStackTraceString(e)); LogUtil.e(e, message); CLog.writeToConsole(Console.MessageLevel.ERROR, Console.MessageSource.JAVASCRIPT, message); } return scope; }
private static void maybeRegister(DescriptorMap map, @Nullable FragmentCompat compat) { if (compat != null) { Class<?> fragmentClass = compat.getFragmentClass(); LogUtil.d("Adding support for %s", fragmentClass.getName()); map.registerDescriptor(fragmentClass, new RIFragmentDescriptor(compat)); } }
public static String getAppVersion() { String appVersion = null; PackageManager packageManager = AndroidApplication.getInstance().getPackageManager(); try { PackageInfo info = packageManager.getPackageInfo(AndroidApplication.getInstance().getPackageName(), 0); appVersion = String.valueOf(info.versionCode); return appVersion; } catch (Throwable e) { LogUtil.d("PhoneInfoUtils.getAppVersion()", e.toString()); } return appVersion; }
private static <T extends Throwable> T handleSuppression(@Nullable T previous, T current) { if (previous == null) { return current; } else { LogUtil.i(TAG, current, "Suppressed while handling " + previous); return previous; } }
private void listenOnAddress(String address) throws IOException { mServerSocket = bindToSocket(address); LogUtil.i("Listening on @" + address); while (!Thread.interrupted()) { try { // Use previously accepted socket the first time around, otherwise wait to // accept another. LocalSocket socket = mServerSocket.accept(); // Start worker thread Thread t = new WorkerThread(socket, mSocketHandler); t.setName( WORKER_THREAD_NAME_PREFIX + "-" + mFriendlyName + "-" + mThreadId.incrementAndGet()); t.setDaemon(true); t.start(); } catch (SocketException se) { // ignore exception if interrupting the thread if (Thread.interrupted()) { break; } LogUtil.w(se, "I/O error"); } catch (InterruptedIOException ex) { break; } catch (IOException e) { LogUtil.w(e, "I/O error initialising connection thread"); break; } } LogUtil.i("Server shutdown on @" + address); }
@Override public void run() { try { mSocketHandler.onAccepted(mSocket); } catch (IOException ex) { LogUtil.w("I/O error: %s", ex); } finally { try { mSocket.close(); } catch (IOException ignore) { } } }
private void startServer(final LocalSocketServer server) { Thread listener = new Thread(THREAD_PREFIX + "-" + server.getName()) { @Override public void run() { try { server.run(); } catch (IOException e) { LogUtil.e(e, "Could not start Stetho server: %s", server.getName()); } } }; listener.start(); }
@Override public final void onAccepted(LocalSocket socket) throws IOException { try { enforcePermission(mContext, socket); onSecured(socket); } catch (PeerAuthorizationException e) { LogUtil.e("Unauthorized request: " + e.getMessage()); } }
@Nonnull public static String getIdStringQuietly(Object idContext, @Nullable Resources r, int resourceId) { try { return getIdString(r, resourceId); } catch (Resources.NotFoundException e) { String idString = getFallbackIdString(resourceId); LogUtil.w("Unknown identifier encountered on " + idContext + ": " + idString); return idString; } }
/** * Start the listening service, providing a custom initializer as per * {@link #newInitializerBuilder}. * * @see #initializeWithDefaults(Context) */ public static void initialize(final Initializer initializer) { // Hook activity tracking so that after Stetho is attached we can figure out what // activities are present. boolean isTrackingActivities = ActivityTracker.get().beginTrackingIfPossible( (Application)initializer.mContext.getApplicationContext()); if (!isTrackingActivities) { LogUtil.w("Automatic activity tracking not available on this API level, caller must invoke " + "ActivityTracker methods manually!"); } initializer.start(); }
public void onInspectRequested(Object element) { Integer nodeId = mDocument.getNodeIdForElement(element); if (nodeId == null) { LogUtil.d( "DocumentProvider.Listener.onInspectRequested() " + "called for a non-mapped node: element=%s", element); } else { InspectNodeRequestedEvent message = new InspectNodeRequestedEvent(); message.nodeId = nodeId; mPeerManager.sendNotificationToPeers("DOM.inspectNodeRequested", message); } }
@ChromeDevtoolsMethod public JsonRpcResult getComputedStyleForNode(JsonRpcPeer peer, JSONObject params) { final GetComputedStyleForNodeRequest request = mObjectMapper.convertValue( params, GetComputedStyleForNodeRequest.class); final GetComputedStyleForNodeResult result = new GetComputedStyleForNodeResult(); result.computedStyle = new ArrayList<>(); mDocument.postAndWait(new Runnable() { @Override public void run() { Object element = mDocument.getElementForNodeId(request.nodeId); if (element == null) { LogUtil.e("Tried to get the style of an element that does not exist, using nodeid=" + request.nodeId); return; } mDocument.getElementComputedStyles( element, new ComputedStyleAccumulator() { @Override public void store(String name, String value) { final CSSComputedStyleProperty property = new CSSComputedStyleProperty(); property.name = name; property.value = value; result.computedStyle.add(property); } }); } }); return result; }
public static ViewHighlighter newInstance() { // TODO: find ways to do highlighting on older versions too if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { return new OverlayHighlighter(); } else { LogUtil.w("Running on pre-JBMR2: View highlighting is not supported"); return new NoopHighlighter(); } }
private static void maybeRegister(DescriptorMap map, @Nullable FragmentCompat compat) { if (compat != null) { Class<?> dialogFragmentClass = compat.getDialogFragmentClass(); LogUtil.d("Adding support for %s", dialogFragmentClass); map.registerDescriptor(dialogFragmentClass, new DialogFragmentDescriptor(compat)); } }
private static void maybeRegister(DescriptorMap map, @Nullable FragmentCompat compat) { if (compat != null) { Class<?> fragmentClass = compat.getFragmentClass(); LogUtil.d("Adding support for %s", fragmentClass.getName()); map.registerDescriptor(fragmentClass, new FragmentDescriptor(compat)); } }
/** * Tries to invoke a method on receiver with a single argument by trying out different types * for arg until it finds one that matches (or not). No exceptions are thrown on failure. * * @param methodName The method name to be invoked * @param argument The single argument to be provided to the method */ public void invoke(Object receiver, String methodName, String argument) { Util.throwIfNull(receiver, methodName, argument); int size = invokers.size(); for (int i = 0; i < size; ++i) { final TypedMethodInvoker<?> invoker = invokers.get(i); if (invoker.invoke(receiver, methodName, argument)) { return; } } LogUtil.w("Method with name " + methodName + " not found for any of the MethodInvoker supported argument types."); }
@Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { Map<String, ?> entries = sharedPreferences.getAll(); boolean existedBefore = mCopy.containsKey(key); boolean existsNow = entries.containsKey(key); Object newValue = existsNow ? entries.get(key) : null; if (existedBefore && existsNow) { signalItemUpdated( mStorageId, key, SharedPreferencesHelper.valueToString(mCopy.get(key)), SharedPreferencesHelper.valueToString(newValue)); mCopy.put(key, newValue); } else if (existedBefore) { signalItemRemoved(mStorageId, key); mCopy.remove(key); } else if (existsNow) { signalItemAdded( mStorageId, key, SharedPreferencesHelper.valueToString(newValue)); mCopy.put(key, newValue); } else { // This can happen due to the async nature of the onSharedPreferenceChanged callback. A // rapid put/remove as two separate commits on a background thread would cause this. LogUtil.i("Detected rapid put/remove of %s", key); } }
public void startScreencast(JsonRpcPeer peer, Page.StartScreencastRequest request) { LogUtil.d("Starting screencast"); mRequest = request; mHandlerThread = new HandlerThread("Screencast Thread"); mHandlerThread.start(); mPeer = peer; mIsRunning = true; mStream = new ByteArrayOutputStream(); mBackgroundHandler = new Handler(mHandlerThread.getLooper()); mMainHandler.postDelayed(mBitmapFetchRunnable, FRAME_DELAY); }
private void updateScreenBitmap() { if (!mIsRunning) { return; } Activity activity = mActivityTracker.tryGetTopActivity(); if (activity == null) { return; } // This stuff needs to happen in the UI thread View rootView = activity.getWindow().getDecorView(); try { if (mBitmap == null) { int viewWidth = rootView.getWidth(); int viewHeight = rootView.getHeight(); float scale = Math.min((float) mRequest.maxWidth / (float) viewWidth, (float) mRequest.maxHeight / (float) viewHeight); int destWidth = (int) (viewWidth * scale); int destHeight = (int) (viewHeight * scale); mBitmap = Bitmap.createBitmap(destWidth, destHeight, Bitmap.Config.RGB_565); mCanvas = new Canvas(mBitmap); Matrix matrix = new Matrix(); mTempSrc.set(0, 0, viewWidth, viewHeight); mTempDst.set(0, 0, destWidth, destHeight); matrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.CENTER); mCanvas.setMatrix(matrix); } rootView.draw(mCanvas); } catch (OutOfMemoryError e) { LogUtil.w("Out of memory trying to allocate screencast Bitmap."); } }
@Override protected void onGetStyles(View element, String ruleName, StyleAccumulator accumulator) { if (VIEW_STYLE_RULE_NAME.equals(ruleName)) { List<ViewCSSProperty> properties = getViewProperties(); for (int i = 0, size = properties.size(); i < size; i++) { ViewCSSProperty property = properties.get(i); try { getStyleFromValue( element, property.getCSSName(), property.getValue(element), property.getAnnotation(), accumulator); } catch (Exception e) { if (e instanceof IllegalAccessException || e instanceof InvocationTargetException) { LogUtil.e(e, "failed to get style property " + property.getCSSName() + " of element= " + element.toString()); } else { throw ExceptionUtil.propagate(e); } } } } else if (ACCESSIBILITY_STYLE_RULE_NAME.equals(ruleName)) { if (sHasSupportNodeInfo) { boolean ignored = AccessibilityNodeInfoWrapper.getIgnored(element); getStyleFromValue( element, "ignored", ignored, null, accumulator); if (ignored) { getStyleFromValue( element, "ignored-reasons", AccessibilityNodeInfoWrapper.getIgnoredReasons(element), null, accumulator); } getStyleFromValue( element, "focusable", !ignored, null, accumulator); if (!ignored) { getStyleFromValue( element, "focusable-reasons", AccessibilityNodeInfoWrapper.getFocusableReasons(element), null, accumulator); getStyleFromValue( element, "focused", AccessibilityNodeInfoWrapper.getIsAccessibilityFocused(element), null, accumulator); getStyleFromValue( element, "description", AccessibilityNodeInfoWrapper.getDescription(element), null, accumulator); getStyleFromValue( element, "actions", AccessibilityNodeInfoWrapper.getActions(element), null, accumulator); } } } }
private void getStylesFromObject( View view, String name, Object value, @Nullable ViewDebug.ExportedProperty annotation, StyleAccumulator styles) { if (annotation == null || !annotation.deepExport() || value == null) { return; } Field[] fields = value.getClass().getFields(); for (Field field : fields) { int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers)) { continue; } Object propertyValue; try { field.setAccessible(true); propertyValue = field.get(value); } catch (IllegalAccessException e) { LogUtil.e( e, "failed to get property of name: \"" + name + "\" of object: " + String.valueOf(value)); return; } String propertyName = field.getName(); switch (propertyName) { case "bottomMargin": propertyName = "margin-bottom"; break; case "topMargin": propertyName = "margin-top"; break; case "leftMargin": propertyName = "margin-left"; break; case "rightMargin": propertyName = "margin-right"; break; default: String annotationPrefix = annotation.prefix(); propertyName = convertViewPropertyNameToCSSName( (annotationPrefix == null) ? propertyName : (annotationPrefix + propertyName)); break; } ViewDebug.ExportedProperty subAnnotation = field.getAnnotation(ViewDebug.ExportedProperty.class); getStyleFromValue( view, propertyName, propertyValue, subAnnotation, styles); } }
private static IOException logAndThrowProtocolException(String message) throws IOException { LogUtil.w(message); throw new IOException(message); }
@ChromeDevtoolsMethod public void releaseObjectGroup(JsonRpcPeer peer, JSONObject params) { LogUtil.w("Ignoring request to releaseObjectGroup: " + params); }
private ShadowDocument.Update createShadowDocumentUpdate() { verifyThreadAccess(); if (mDocumentProvider.getRootElement() != mShadowDocument.getRootElement()) { throw new IllegalStateException(); } ArrayListAccumulator<Object> childrenAccumulator = acquireChildrenAccumulator(); ShadowDocument.UpdateBuilder updateBuilder = mShadowDocument.beginUpdate(); mCachedUpdateQueue.add(mDocumentProvider.getRootElement()); while (!mCachedUpdateQueue.isEmpty()) { final Object element = mCachedUpdateQueue.remove(); NodeDescriptor descriptor = mDocumentProvider.getNodeDescriptor(element); mObjectIdMapper.putObject(element); descriptor.getChildren(element, childrenAccumulator); for (int i = 0, size = childrenAccumulator.size(); i < size; ++i) { Object child = childrenAccumulator.get(i); if (child != null) { mCachedUpdateQueue.add(child); } else { // This could be indicative of a bug in Stetho code, but could also be caused by a // custom element of some kind, e.g. ViewGroup. Let's not allow it to kill the hosting // app. LogUtil.e( "%s.getChildren() emitted a null child at position %s for element %s", descriptor.getClass().getName(), Integer.toString(i), element); childrenAccumulator.remove(i); --i; --size; } } updateBuilder.setElementChildren(element, childrenAccumulator); childrenAccumulator.clear(); } releaseChildrenAccumulator(childrenAccumulator); return updateBuilder.build(); }
public void stopScreencast() { LogUtil.d("Stopping screencast"); mBackgroundHandler.post(new CancellationRunnable()); }