blob: 17ba06eafb6f548ae6091ac3b8a237d9088117d5 [file] [log] [blame]
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/profiles/profile_metrics.h"
#include <string>
#include <vector>
#include "base/check_op.h"
#include "base/containers/flat_map.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/common/chrome_constants.h"
#include "components/profile_metrics/counts.h"
namespace {
// Enum for getting net counts for adding and deleting users.
enum class ProfileNetUserCounts {
ADD_NEW_USER = 0, // Total count of add new user
PROFILE_DELETED, // User deleted a profile
kMaxValue = PROFILE_DELETED
};
// Count of profiles sharing a particular name.
struct ProfileCountByName {
explicit ProfileCountByName(bool is_managed)
: managed_count(is_managed ? 1 : 0),
non_managed_count(is_managed ? 0 : 1) {}
ProfileCountByName(const ProfileCountByName&) = default;
ProfileCountByName& operator=(const ProfileCountByName&) = default;
int managed_count = 0;
int non_managed_count = 0;
};
base::flat_map<std::u16string, ProfileCountByName> GetProfilesByGaiaName(
const ProfileAttributesStorage& storage) {
base::flat_map<std::u16string, ProfileCountByName> profile_counts_by_name;
for (const auto* entry : storage.GetAllProfilesAttributes()) {
const std::u16string gaia_name = entry->GetGAIAGivenName();
if (gaia_name.empty()) {
continue;
}
const bool is_managed = entry->UserAcceptedAccountManagement();
auto it = profile_counts_by_name.find(gaia_name);
if (it == profile_counts_by_name.end()) {
profile_counts_by_name.emplace(gaia_name, is_managed);
continue;
}
ProfileCountByName& count = it->second;
if (is_managed) {
++count.managed_count;
} else {
++count.non_managed_count;
}
}
return profile_counts_by_name;
}
// Count and return summary information about the profiles currently in the
// `storage`.
profile_metrics::Counts CountProfileInformation(
ProfileAttributesStorage* storage,
profile_metrics::ProfileActivityThreshold activity_threshold) {
profile_metrics::Counts counts;
size_t number_of_profiles = storage->GetNumberOfProfiles();
counts.total = number_of_profiles;
// Ignore other metrics if we have no profiles.
if (!number_of_profiles) {
return counts;
}
std::vector<ProfileAttributesEntry*> entries =
storage->GetAllProfilesAttributes();
for (ProfileAttributesEntry* entry : entries) {
if (!ProfileMetrics::IsProfileActive(entry, activity_threshold)) {
counts.unused++;
} else {
counts.active++;
if (entry->IsSupervised()) {
counts.supervised++;
}
if (entry->IsAuthenticated()) {
counts.signedin++;
}
}
}
return counts;
}
#if !BUILDFLAG(IS_ANDROID)
base::TimeDelta GetActivityThresholdDelta(
profile_metrics::ProfileActivityThreshold activity_threshold) {
switch (activity_threshold) {
case profile_metrics::ProfileActivityThreshold::kDuration1Day:
return base::Days(1);
case profile_metrics::ProfileActivityThreshold::kDuration7Days:
return base::Days(7);
case profile_metrics::ProfileActivityThreshold::kDuration28Days:
return base::Days(28);
}
}
#endif // !BUILDFLAG(IS_ANDROID)
} // namespace
// This enum is used for histograms. Do not change existing values. Append new
// values at the end.
enum ProfileAvatar {
AVATAR_GENERIC = 0, // The names for avatar icons
AVATAR_GENERIC_AQUA = 1,
AVATAR_GENERIC_BLUE = 2,
AVATAR_GENERIC_GREEN = 3,
AVATAR_GENERIC_ORANGE = 4,
AVATAR_GENERIC_PURPLE = 5,
AVATAR_GENERIC_RED = 6,
AVATAR_GENERIC_YELLOW = 7,
AVATAR_SECRET_AGENT = 8,
AVATAR_SUPERHERO = 9,
AVATAR_VOLLEYBALL = 10,
AVATAR_BUSINESSMAN = 11,
AVATAR_NINJA = 12,
AVATAR_ALIEN = 13,
AVATAR_AWESOME = 14,
AVATAR_FLOWER = 15,
AVATAR_PIZZA = 16,
AVATAR_SOCCER = 17,
AVATAR_BURGER = 18,
AVATAR_CAT = 19,
AVATAR_CUPCAKE = 20,
AVATAR_DOG = 21,
AVATAR_HORSE = 22,
AVATAR_MARGARITA = 23,
AVATAR_NOTE = 24,
AVATAR_SUN_CLOUD = 25,
AVATAR_PLACEHOLDER = 26,
AVATAR_UNKNOWN = 27,
AVATAR_GAIA = 28,
// Modern avatars:
AVATAR_ORIGAMI_CAT = 29,
AVATAR_ORIGAMI_CORGI = 30,
AVATAR_ORIGAMI_DRAGON = 31,
AVATAR_ORIGAMI_ELEPHANT = 32,
AVATAR_ORIGAMI_FOX = 33,
AVATAR_ORIGAMI_MONKEY = 34,
AVATAR_ORIGAMI_PANDA = 35,
AVATAR_ORIGAMI_PENGUIN = 36,
AVATAR_ORIGAMI_PINKBUTTERFLY = 37,
AVATAR_ORIGAMI_RABBIT = 38,
AVATAR_ORIGAMI_UNICORN = 39,
AVATAR_ILLUSTRATION_BASKETBALL = 40,
AVATAR_ILLUSTRATION_BIKE = 41,
AVATAR_ILLUSTRATION_BIRD = 42,
AVATAR_ILLUSTRATION_CHEESE = 43,
AVATAR_ILLUSTRATION_FOOTBALL = 44,
AVATAR_ILLUSTRATION_RAMEN = 45,
AVATAR_ILLUSTRATION_SUNGLASSES = 46,
AVATAR_ILLUSTRATION_SUSHI = 47,
AVATAR_ILLUSTRATION_TAMAGOTCHI = 48,
AVATAR_ILLUSTRATION_VINYL = 49,
AVATAR_ABSTRACT_AVOCADO = 50,
AVATAR_ABSTRACT_CAPPUCCINO = 51,
AVATAR_ABSTRACT_ICECREAM = 52,
AVATAR_ABSTRACT_ICEWATER = 53,
AVATAR_ABSTRACT_MELON = 54,
AVATAR_ABSTRACT_ONIGIRI = 55,
AVATAR_ABSTRACT_PIZZA = 56,
AVATAR_ABSTRACT_SANDWICH = 57,
NUM_PROFILE_AVATAR_METRICS
};
// static
bool ProfileMetrics::IsProfileActive(
const ProfileAttributesEntry* entry,
profile_metrics::ProfileActivityThreshold activity_threshold) {
#if !BUILDFLAG(IS_ANDROID)
// TODO(mlerman): iOS and Android should set an ActiveTime in the
// ProfileAttributesStorage. (see ProfileManager::OnBrowserSetLastActive)
if (base::Time::Now() - entry->GetActiveTime() >
GetActivityThresholdDelta(activity_threshold)) {
return false;
}
#endif
return true;
}
void ProfileMetrics::LogNumberOfProfiles(ProfileAttributesStorage* storage) {
CHECK(storage);
profile_metrics::LogTotalNumberOfProfiles(storage->GetNumberOfProfiles());
for (profile_metrics::ProfileActivityThreshold activity_threshold :
{profile_metrics::ProfileActivityThreshold::kDuration1Day,
profile_metrics::ProfileActivityThreshold::kDuration7Days,
profile_metrics::ProfileActivityThreshold::kDuration28Days}) {
profile_metrics::Counts counts =
CountProfileInformation(storage, activity_threshold);
profile_metrics::LogProfileMetricsCounts(counts, activity_threshold);
}
// Records whether some profiles have primary accounts with the same first
// name.
base::flat_map<std::u16string, ProfileCountByName> profile_counts_by_name =
GetProfilesByGaiaName(*storage);
for (const auto& [name, count] : profile_counts_by_name) {
GaiaNameShareStatus name_shared = GaiaNameShareStatus::kNotShared;
if (count.non_managed_count > 1) {
name_shared = GaiaNameShareStatus::kSharedNonManaged;
} else if (count.non_managed_count + count.managed_count > 1) {
name_shared = GaiaNameShareStatus::kSharedManaged;
}
base::UmaHistogramEnumeration("Profile.GaiaNameShareStatus", name_shared);
}
}
void ProfileMetrics::LogProfileAddNewUser(ProfileAdd metric) {
base::UmaHistogramEnumeration("Profile.AddNewUser", metric);
base::UmaHistogramEnumeration("Profile.NetUserCount",
ProfileNetUserCounts::ADD_NEW_USER);
}
// static
void ProfileMetrics::LogProfileAddSignInFlowOutcome(
ProfileSignedInFlowOutcome outcome) {
base::UmaHistogramEnumeration("Profile.AddSignInFlowOutcome", outcome);
}
void ProfileMetrics::LogProfileAvatarSelection(size_t icon_index) {
ProfileAvatar icon_name = AVATAR_UNKNOWN;
switch (icon_index) {
case 0:
icon_name = AVATAR_GENERIC;
break;
case 1:
icon_name = AVATAR_GENERIC_AQUA;
break;
case 2:
icon_name = AVATAR_GENERIC_BLUE;
break;
case 3:
icon_name = AVATAR_GENERIC_GREEN;
break;
case 4:
icon_name = AVATAR_GENERIC_ORANGE;
break;
case 5:
icon_name = AVATAR_GENERIC_PURPLE;
break;
case 6:
icon_name = AVATAR_GENERIC_RED;
break;
case 7:
icon_name = AVATAR_GENERIC_YELLOW;
break;
case 8:
icon_name = AVATAR_SECRET_AGENT;
break;
case 9:
icon_name = AVATAR_SUPERHERO;
break;
case 10:
icon_name = AVATAR_VOLLEYBALL;
break;
case 11:
icon_name = AVATAR_BUSINESSMAN;
break;
case 12:
icon_name = AVATAR_NINJA;
break;
case 13:
icon_name = AVATAR_ALIEN;
break;
case 14:
icon_name = AVATAR_AWESOME;
break;
case 15:
icon_name = AVATAR_FLOWER;
break;
case 16:
icon_name = AVATAR_PIZZA;
break;
case 17:
icon_name = AVATAR_SOCCER;
break;
case 18:
icon_name = AVATAR_BURGER;
break;
case 19:
icon_name = AVATAR_CAT;
break;
case 20:
icon_name = AVATAR_CUPCAKE;
break;
case 21:
icon_name = AVATAR_DOG;
break;
case 22:
icon_name = AVATAR_HORSE;
break;
case 23:
icon_name = AVATAR_MARGARITA;
break;
case 24:
icon_name = AVATAR_NOTE;
break;
case 25:
icon_name = AVATAR_SUN_CLOUD;
break;
case 26:
icon_name = AVATAR_PLACEHOLDER;
break;
// Modern avatars:
case 27:
icon_name = AVATAR_ORIGAMI_CAT;
break;
case 28:
icon_name = AVATAR_ORIGAMI_CORGI;
break;
case 29:
icon_name = AVATAR_ORIGAMI_DRAGON;
break;
case 30:
icon_name = AVATAR_ORIGAMI_ELEPHANT;
break;
case 31:
icon_name = AVATAR_ORIGAMI_FOX;
break;
case 32:
icon_name = AVATAR_ORIGAMI_MONKEY;
break;
case 33:
icon_name = AVATAR_ORIGAMI_PANDA;
break;
case 34:
icon_name = AVATAR_ORIGAMI_PENGUIN;
break;
case 35:
icon_name = AVATAR_ORIGAMI_PINKBUTTERFLY;
break;
case 36:
icon_name = AVATAR_ORIGAMI_RABBIT;
break;
case 37:
icon_name = AVATAR_ORIGAMI_UNICORN;
break;
case 38:
icon_name = AVATAR_ILLUSTRATION_BASKETBALL;
break;
case 39:
icon_name = AVATAR_ILLUSTRATION_BIKE;
break;
case 40:
icon_name = AVATAR_ILLUSTRATION_BIRD;
break;
case 41:
icon_name = AVATAR_ILLUSTRATION_CHEESE;
break;
case 42:
icon_name = AVATAR_ILLUSTRATION_FOOTBALL;
break;
case 43:
icon_name = AVATAR_ILLUSTRATION_RAMEN;
break;
case 44:
icon_name = AVATAR_ILLUSTRATION_SUNGLASSES;
break;
case 45:
icon_name = AVATAR_ILLUSTRATION_SUSHI;
break;
case 46:
icon_name = AVATAR_ILLUSTRATION_TAMAGOTCHI;
break;
case 47:
icon_name = AVATAR_ILLUSTRATION_VINYL;
break;
case 48:
icon_name = AVATAR_ABSTRACT_AVOCADO;
break;
case 49:
icon_name = AVATAR_ABSTRACT_CAPPUCCINO;
break;
case 50:
icon_name = AVATAR_ABSTRACT_ICECREAM;
break;
case 51:
icon_name = AVATAR_ABSTRACT_ICEWATER;
break;
case 52:
icon_name = AVATAR_ABSTRACT_MELON;
break;
case 53:
icon_name = AVATAR_ABSTRACT_ONIGIRI;
break;
case 54:
icon_name = AVATAR_ABSTRACT_PIZZA;
break;
case 55:
icon_name = AVATAR_ABSTRACT_SANDWICH;
break;
case SIZE_MAX:
icon_name = AVATAR_GAIA;
break;
default:
NOTREACHED();
}
base::UmaHistogramEnumeration("Profile.Avatar", icon_name,
NUM_PROFILE_AVATAR_METRICS);
}
void ProfileMetrics::LogProfileDeleteUser(ProfileDelete metric) {
DCHECK(metric < NUM_DELETE_PROFILE_METRICS);
base::UmaHistogramEnumeration("Profile.DeleteProfileAction", metric,
NUM_DELETE_PROFILE_METRICS);
if (metric != DELETE_PROFILE_USER_MANAGER_SHOW_WARNING &&
metric != DELETE_PROFILE_SETTINGS_SHOW_WARNING &&
metric != DELETE_PROFILE_ABORTED) {
// If a user was actually deleted, update the net user count.
base::UmaHistogramEnumeration("Profile.NetUserCount",
ProfileNetUserCounts::PROFILE_DELETED);
}
}
void ProfileMetrics::LogProfileLaunch(Profile* profile) {
if (profile->IsChild()) {
base::RecordAction(
base::UserMetricsAction("ManagedMode_NewManagedUserWindow"));
}
}