Ejemplo n.º 1
0
  public DistributionDescriptor getDescriptor() {
    File descFile = getDistributionFile("preferences.json");
    if (descFile == null) {
      // Logging and existence checks are handled in getDistributionFile.
      return null;
    }

    try {
      JSONObject all = FileUtils.readJSONObjectFromFile(descFile);

      if (!all.has("Global")) {
        Log.e(LOGTAG, "Distribution preferences.json has no Global entry!");
        return null;
      }

      return new DistributionDescriptor(all.getJSONObject("Global"));

    } catch (IOException e) {
      Log.e(LOGTAG, "Error getting distribution descriptor file.", e);
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_MALFORMED_DISTRIBUTION);
      return null;
    } catch (JSONException e) {
      Log.e(LOGTAG, "Error parsing preferences.json", e);
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_MALFORMED_DISTRIBUTION);
      return null;
    }
  }
Ejemplo n.º 2
0
  /**
   * Get the Android preferences from the preferences.json file, if any exist.
   *
   * @return The preferences in a JSONObject, or an empty JSONObject if no preferences are defined.
   */
  public JSONObject getAndroidPreferences() {
    final File descFile = getDistributionFile("preferences.json");
    if (descFile == null) {
      // Logging and existence checks are handled in getDistributionFile.
      return new JSONObject();
    }

    try {
      final JSONObject all = FileUtils.readJSONObjectFromFile(descFile);

      if (!all.has("AndroidPreferences")) {
        return new JSONObject();
      }

      return all.getJSONObject("AndroidPreferences");

    } catch (IOException e) {
      Log.e(LOGTAG, "Error getting distribution descriptor file.", e);
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_MALFORMED_DISTRIBUTION);
      return new JSONObject();
    } catch (JSONException e) {
      Log.e(LOGTAG, "Error parsing preferences.json", e);
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_MALFORMED_DISTRIBUTION);
      return new JSONObject();
    }
  }
Ejemplo n.º 3
0
  /**
   * Fetch the provided URI, returning a {@link JarInputStream} if the response body is appropriate.
   *
   * <p>Protected to allow for mocking.
   *
   * @return the entity body as a stream, or null on failure.
   */
  @SuppressWarnings("static-method")
  @RobocopTarget
  protected JarInputStream fetchDistribution(URI uri, HttpURLConnection connection)
      throws IOException {
    final int status = connection.getResponseCode();

    Log.d(LOGTAG, "Distribution fetch: " + status);
    // We record HTTP statuses as 2xx, 3xx, 4xx, 5xx => 2, 3, 4, 5.
    final int value;
    if (status > 599 || status < 100) {
      Log.wtf(LOGTAG, "Unexpected HTTP status code: " + status);
      value = CODE_CATEGORY_STATUS_OUT_OF_RANGE;
    } else {
      value = status / 100;
    }

    Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, value);

    if (status != 200) {
      Log.w(LOGTAG, "Got status " + status + " fetching distribution.");
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_FETCH_NON_SUCCESS_RESPONSE);
      return null;
    }

    final String contentType = connection.getContentType();
    if (contentType == null || !contentType.startsWith(EXPECTED_CONTENT_TYPE)) {
      Log.w(LOGTAG, "Malformed response: invalid Content-Type.");
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_FETCH_INVALID_CONTENT_TYPE);
      return null;
    }

    return new JarInputStream(new BufferedInputStream(connection.getInputStream()), true);
  }
Ejemplo n.º 4
0
  private static void recordFetchTelemetry(final Exception exception) {
    if (exception == null) {
      // Should never happen.
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_FETCH_EXCEPTION);
      return;
    }

    if (exception instanceof UnknownHostException) {
      // Unknown host => we're offline.
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_OFFLINE);
      return;
    }

    if (exception instanceof SSLException) {
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_FETCH_SSL_ERROR);
      return;
    }

    if (exception instanceof ProtocolException || exception instanceof SocketException) {
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_FETCH_SOCKET_ERROR);
      return;
    }

    Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_FETCH_EXCEPTION);
  }
Ejemplo n.º 5
0
  private void addTab() {
    if (mCurrentPanel == Panel.NORMAL_TABS) {
      Telemetry.sendUIEvent(
          TelemetryContract.Event.ACTION, TelemetryContract.Method.ACTIONBAR, "new_tab");
      mActivity.addTab();
    } else {
      Telemetry.sendUIEvent(
          TelemetryContract.Event.ACTION, TelemetryContract.Method.ACTIONBAR, "new_private_tab");
      mActivity.addPrivateTab();
    }

    mActivity.autoHideTabs();
  }
  /**
   * Update the hide/show state of the preference and save the HomeConfig changes.
   *
   * @param pref Preference to update
   * @param toHide New hidden state of the preference
   */
  protected void setHidden(PanelsPreference pref, boolean toHide) {
    final String id = pref.getKey();
    mConfigEditor.setDisabled(id, toHide);
    mConfigEditor.apply();

    if (toHide) {
      Telemetry.sendUIEvent(TelemetryContract.Event.PANEL_HIDE, Method.DIALOG, id);
    } else {
      Telemetry.sendUIEvent(TelemetryContract.Event.PANEL_SHOW, Method.DIALOG, id);
    }

    pref.setHidden(toHide);
    setDefaultFromConfig();
  }
Ejemplo n.º 7
0
  @Override
  public void onReceive(final Context context, final Intent intent) {
    // This will hold the intent to redispatch
    final Intent redirect;
    if (intent.getAction().equals(ACTION_LAUNCH_BROWSER)) {
      redirect =
          buildRedirectIntent(
              Intent.ACTION_MAIN,
              AppConstants.ANDROID_PACKAGE_NAME,
              AppConstants.BROWSER_INTENT_CLASS_NAME,
              intent);
      Telemetry.sendUIEvent(
          TelemetryContract.Event.LAUNCH, TelemetryContract.Method.WIDGET, "browser");
    } else if (intent.getAction().equals(ACTION_LAUNCH_NEW_TAB)) {
      redirect =
          buildRedirectIntent(
              Intent.ACTION_VIEW,
              AppConstants.ANDROID_PACKAGE_NAME,
              AppConstants.BROWSER_INTENT_CLASS_NAME,
              intent);
      Telemetry.sendUIEvent(
          TelemetryContract.Event.LAUNCH, TelemetryContract.Method.WIDGET, "new-tab");
    } else if (intent.getAction().equals(ACTION_LAUNCH_SEARCH)) {
      redirect =
          buildRedirectIntent(
              Intent.ACTION_VIEW,
              AppConstants.SEARCH_PACKAGE_NAME,
              AppConstants.SEARCH_INTENT_CLASS_NAME,
              intent);
      Telemetry.sendUIEvent(
          TelemetryContract.Event.LAUNCH, TelemetryContract.Method.WIDGET, "search");
    } else {
      redirect = null;
    }

    if (redirect != null) {
      try {
        context.startActivity(redirect);
      } catch (Exception ex) {
        // When this is built stand alone, its hardcoded to try and launch nightly.
        // If that fails, just fire a generic VIEW intent.
        Intent redirect2 = buildRedirectIntent(Intent.ACTION_VIEW, null, null, intent);
        context.startActivity(redirect2);
      }
    }

    super.onReceive(context, intent);
  }
  @Override
  public void onItemClicked(RecyclerView recyclerView, int position, View v) {
    final int viewType = getAdapter().getItemViewType(position);
    final CombinedHistoryAdapter.ItemType itemType =
        CombinedHistoryAdapter.ItemType.viewTypeToItemType(viewType);

    switch (itemType) {
      case CLIENT:
        mOnPanelLevelChangeListener.onPanelLevelChange(PanelLevel.CHILD);
        ((CombinedHistoryAdapter) getAdapter()).showChildView(position);
        break;
      case HIDDEN_DEVICES:
        if (mDialogBuilder != null) {
          mDialogBuilder.createAndShowDialog(
              ((CombinedHistoryAdapter) getAdapter()).getHiddenClients());
        }
        break;

      case NAVIGATION_BACK:
        mOnPanelLevelChangeListener.onPanelLevelChange(PARENT);
        ((CombinedHistoryAdapter) getAdapter()).exitChildView();
        break;
      case CHILD:
      case HISTORY:
        if (mOnUrlOpenListener != null) {
          final TwoLinePageRow historyItem = (TwoLinePageRow) v;
          Telemetry.sendUIEvent(
              TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM, "history");
          mOnUrlOpenListener.onUrlOpen(
              historyItem.getUrl(),
              EnumSet.of(HomePager.OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
        }
        break;
    }
  }
Ejemplo n.º 9
0
  private void openNow(Intent intent) {
    Intent forwardIntent = new Intent(intent);
    forwardIntent.setClassName(
        getApplicationContext(), AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
    forwardIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(forwardIntent);

    TabQueueHelper.removeNotification(getApplicationContext());

    GeckoSharedPrefs.forApp(getApplicationContext())
        .edit()
        .remove(GeckoPreferences.PREFS_TAB_QUEUE_LAST_SITE)
        .remove(GeckoPreferences.PREFS_TAB_QUEUE_LAST_TIME)
        .apply();

    Telemetry.sendUIEvent(
        TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "tabqueue-now");

    executorService.submit(
        new Runnable() {
          @Override
          public void run() {
            int queuedTabCount = TabQueueHelper.getTabQueueLength(TabQueueService.this);
            Telemetry.addToHistogram("FENNEC_TABQUEUE_QUEUESIZE", queuedTabCount);
          }
        });
  }
Ejemplo n.º 10
0
  public void load(
      Context appContext,
      FragmentManager fm,
      final FirstrunAnimationContainer.OnFinishListener onFinishListener) {
    final List<FirstrunPagerConfig.FirstrunPanelConfig> panels;

    if (Restrictions.isUserRestricted(context)) {
      panels = FirstrunPagerConfig.getRestricted();
    } else {
      panels = FirstrunPagerConfig.getDefault(appContext);
      if (panels.size() == 1) {
        mTabStrip.setVisibility(GONE);
      }
    }

    setAdapter(new ViewPagerAdapter(fm, panels));
    this.pagerNavigation =
        new FirstrunPanel.PagerNavigation() {
          @Override
          public void next() {
            final int currentPage = FirstrunPager.this.getCurrentItem();
            if (currentPage < FirstrunPager.this.getAdapter().getCount() - 1) {
              FirstrunPager.this.setCurrentItem(currentPage + 1);
            }
          }

          @Override
          public void finish() {
            if (onFinishListener != null) {
              onFinishListener.onFinish();
            }
          }
        };
    addOnPageChangeListener(
        new OnPageChangeListener() {
          @Override
          public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            mDecor.onPageScrolled(position, positionOffset, positionOffsetPixels);
          }

          @Override
          public void onPageSelected(int i) {
            mDecor.onPageSelected(i);
            Telemetry.sendUIEvent(
                TelemetryContract.Event.SHOW, TelemetryContract.Method.PANEL, "onboarding." + i);
          }

          @Override
          public void onPageScrollStateChanged(int i) {}
        });

    animateLoad();

    // Record telemetry for first onboarding panel, for baseline.
    Telemetry.sendUIEvent(
        TelemetryContract.Event.SHOW, TelemetryContract.Method.PANEL, "onboarding.0");
  }
Ejemplo n.º 11
0
  public JSONArray getBookmarks() {
    File bookmarks = getDistributionFile("bookmarks.json");
    if (bookmarks == null) {
      // Logging and existence checks are handled in getDistributionFile.
      return null;
    }

    try {
      return new JSONArray(FileUtils.readStringFromFile(bookmarks));
    } catch (IOException e) {
      Log.e(LOGTAG, "Error getting bookmarks", e);
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_MALFORMED_DISTRIBUTION);
      return null;
    } catch (JSONException e) {
      Log.e(LOGTAG, "Error parsing bookmarks.json", e);
      Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_MALFORMED_DISTRIBUTION);
      return null;
    }
  }
  @Override
  public void uninstall(CustomListPreference pref) {
    final String id = pref.getKey();
    mConfigEditor.uninstall(id);
    mConfigEditor.apply();

    Telemetry.sendUIEvent(TelemetryContract.Event.PANEL_REMOVE, Method.DIALOG, id);

    super.uninstall(pref);
  }
  public void moveDown(PanelsPreference pref) {
    final int panelIndex = pref.getIndex();
    if (panelIndex < getPreferenceCount() - 1) {
      final String panelKey = pref.getKey();
      mConfigEditor.moveTo(panelKey, panelIndex + 1);
      final State state = mConfigEditor.apply();

      Telemetry.sendUIEvent(TelemetryContract.Event.PANEL_MOVE, Method.DIALOG, panelKey);

      refresh(state, panelKey);
    }
  }
Ejemplo n.º 14
0
  @Override
  public boolean onMenuItemClick(MenuItem item) {
    final int itemId = item.getItemId();

    if (itemId == R.id.close_all_tabs) {
      if (mCurrentPanel == Panel.NORMAL_TABS) {
        final String extras = getResources().getResourceEntryName(itemId);
        Telemetry.sendUIEvent(
            TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, extras);

        // Disable the menu button so that the menu won't interfere with the tab close animation.
        mMenuButton.setEnabled(false);
        ((CloseAllPanelView) mPanelNormal).closeAll();
      } else {
        Log.e(LOGTAG, "Close all tabs menu item should only be visible for normal tabs panel");
      }
      return true;
    }

    if (itemId == R.id.close_private_tabs) {
      if (mCurrentPanel == Panel.PRIVATE_TABS) {
        final String extras = getResources().getResourceEntryName(itemId);
        Telemetry.sendUIEvent(
            TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, extras);

        ((CloseAllPanelView) mPanelPrivate).closeAll();
      } else {
        Log.e(LOGTAG, "Close private tabs menu item should only be visible for private tabs panel");
      }
      return true;
    }

    if (itemId == R.id.new_tab || itemId == R.id.new_private_tab) {
      hide();
    }

    return mActivity.onOptionsItemSelected(item);
  }
  @Override
  public void setDefault(CustomListPreference pref) {
    super.setDefault(pref);

    final String id = pref.getKey();

    final String defaultPanelId = mConfigEditor.getDefaultPanelId();
    if (defaultPanelId != null && defaultPanelId.equals(id)) {
      return;
    }

    mConfigEditor.setDefault(id);
    mConfigEditor.apply();

    Telemetry.sendUIEvent(TelemetryContract.Event.PANEL_SET_DEFAULT, Method.DIALOG, id);
  }
Ejemplo n.º 16
0
  private URI getReferredDistribution(ReferrerDescriptor descriptor) {
    final String content = descriptor.content;
    if (content == null) {
      return null;
    }

    // We restrict here to avoid injection attacks. After all,
    // we're downloading a distribution payload based on intent input.
    if (!content.matches("^[a-zA-Z0-9]+$")) {
      Log.e(LOGTAG, "Invalid referrer content: " + content);
      Telemetry.addToHistogram(HISTOGRAM_REFERRER_INVALID, 1);
      return null;
    }

    try {
      return new URI(FETCH_PROTOCOL, FETCH_HOSTNAME, FETCH_PATH + content + FETCH_EXTENSION, null);
    } catch (URISyntaxException e) {
      // This should never occur.
      Log.wtf(LOGTAG, "Invalid URI with content " + content + "!");
      return null;
    }
  }
Ejemplo n.º 17
0
  /**
   * If applicable, download and select the distribution specified in the referrer intent.
   *
   * @return true if a referrer-supplied distribution was selected.
   */
  private boolean checkIntentDistribution(final ReferrerDescriptor referrer) {
    if (referrer == null) {
      return false;
    }

    URI uri = getReferredDistribution(referrer);
    if (uri == null) {
      return false;
    }

    long start = SystemClock.uptimeMillis();
    Log.v(LOGTAG, "Downloading referred distribution: " + uri);

    try {
      final HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();

      // If the Search Activity starts, and we handle the referrer intent, this'll return
      // null. Recover gracefully in this case.
      final GeckoAppShell.GeckoInterface geckoInterface = GeckoAppShell.getGeckoInterface();
      final String ua;
      if (geckoInterface == null) {
        // Fall back to GeckoApp's default implementation.
        ua =
            HardwareUtils.isTablet()
                ? AppConstants.USER_AGENT_FENNEC_TABLET
                : AppConstants.USER_AGENT_FENNEC_MOBILE;
      } else {
        ua = geckoInterface.getDefaultUAString();
      }

      connection.setRequestProperty(HTTP.USER_AGENT, ua);
      connection.setRequestProperty("Accept", EXPECTED_CONTENT_TYPE);

      try {
        final JarInputStream distro;
        try {
          distro = fetchDistribution(uri, connection);
        } catch (Exception e) {
          Log.e(LOGTAG, "Error fetching distribution from network.", e);
          recordFetchTelemetry(e);
          return false;
        }

        long end = SystemClock.uptimeMillis();
        final long duration = end - start;
        Log.d(LOGTAG, "Distro fetch took " + duration + "ms; result? " + (distro != null));
        Telemetry.addToHistogram(
            HISTOGRAM_DOWNLOAD_TIME_MS, clamp(MAX_DOWNLOAD_TIME_MSEC, duration));

        if (distro == null) {
          // Nothing to do.
          return false;
        }

        // Try to copy distribution files from the fetched stream.
        try {
          Log.d(LOGTAG, "Copying files from fetched zip.");
          if (copyFilesFromStream(distro)) {
            // We always copy to the data dir, and we only copy files from
            // a 'distribution' subdirectory. Now determine our actual distribution directory.
            return checkDataDistribution();
          }
        } catch (SecurityException e) {
          Log.e(LOGTAG, "Security exception copying files. Corrupt or malicious?", e);
          Telemetry.addToHistogram(
              HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_POST_FETCH_SECURITY_EXCEPTION);
        } catch (Exception e) {
          Log.e(LOGTAG, "Error copying files from distribution.", e);
          Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, CODE_CATEGORY_POST_FETCH_EXCEPTION);
        } finally {
          distro.close();
        }
      } finally {
        connection.disconnect();
      }
    } catch (IOException e) {
      Log.e(LOGTAG, "Error copying distribution files from network.", e);
      recordFetchTelemetry(e);
    }

    return false;
  }
Ejemplo n.º 18
0
  /**
   * Returns an unmodifiable Map of url->Metadata (i.e. A second HashMap) for a list of urls. Must
   * not be called from UI or Gecko threads.
   */
  public static Map<String, Map<String, Object>> getForUrls(
      final ContentResolver cr, final List<String> urls, final List<String> columns) {
    ThreadUtils.assertNotOnUiThread();
    ThreadUtils.assertNotOnGeckoThread();

    final Map<String, Map<String, Object>> data = new HashMap<String, Map<String, Object>>();

    // Nothing to query for
    if (urls.isEmpty() || columns.isEmpty()) {
      Log.e(LOGTAG, "Queried metadata for nothing");
      return data;
    }

    // Search the cache for any of these urls
    List<String> urlsToQuery = new ArrayList<String>();
    for (String url : urls) {
      final Map<String, Object> hit = cache.get(url);
      if (hit != null) {
        // Cache hit!
        data.put(url, hit);
      } else {
        urlsToQuery.add(url);
      }
    }

    Telemetry.HistogramAdd("FENNEC_TILES_CACHE_HIT", data.size());

    // If everything was in the cache, we're done!
    if (urlsToQuery.size() == 0) {
      return Collections.unmodifiableMap(data);
    }

    final String selection =
        DBUtils.computeSQLInClause(urlsToQuery.size(), URLMetadataTable.URL_COLUMN);
    // We need the url to build our final HashMap, so we force it to be included in the query.
    if (!columns.contains(URLMetadataTable.URL_COLUMN)) {
      columns.add(URLMetadataTable.URL_COLUMN);
    }

    final Cursor cursor =
        cr.query(
            URLMetadataTable.CONTENT_URI,
            columns.toArray(new String[columns.size()]), // columns,
            selection, // selection
            urlsToQuery.toArray(new String[urlsToQuery.size()]), // selectionargs
            null);
    try {
      if (!cursor.moveToFirst()) {
        return Collections.unmodifiableMap(data);
      }

      do {
        final Map<String, Object> metadata = fromCursor(cursor);
        final String url =
            cursor.getString(cursor.getColumnIndexOrThrow(URLMetadataTable.URL_COLUMN));

        data.put(url, metadata);
        cache.put(url, metadata);
      } while (cursor.moveToNext());

    } finally {
      cursor.close();
    }

    return Collections.unmodifiableMap(data);
  }